Skip to content

Plugins

TerraCi is built as a plugin-first system. Every feature — pipeline generation, cost estimation, policy checks, MR comments — is a plugin. You can add your own plugins to integrate with any tool or service your team uses.

What Can Plugins Do?

Plugin TypeWhat It AddsExample Use Cases
CLI CommandNew terraci <command> subcommandSlack notifications, custom reports, infra audits
Pipeline StepJobs/steps injected into generated CI pipelinesSecurity scans, compliance checks, deployment gates
CI ProviderSupport for a new CI system (beyond GitLab/GitHub)Bitbucket Pipelines, Jenkins, CircleCI
Init Wizard FieldConfiguration fields in terraci init TUICustom plugin settings, team-specific defaults

Quick Start

Build a custom TerraCi with your plugin in 3 steps:

bash
# 1. Write your plugin (see guides below)

# 2. Build a custom binary
xterraci build --with github.com/your-org/terraci-plugin-slack

# 3. Use it
./terraci slack --channel #deploys

Architecture

                    ┌──────────────────────────┐
                    │      TerraCi Core         │
                    │                           │
                    │  discovery → parser →     │
                    │  graph → pipeline IR      │
                    └──────────┬───────────────┘

              ┌────────────────┼────────────────┐
              │                │                │
        ┌─────┴─────┐   ┌─────┴─────┐   ┌─────┴─────┐
        │  Built-in  │   │  Built-in  │   │  Your     │
        │  Plugins   │   │  Plugins   │   │  Plugin   │
        │            │   │            │   │           │
        │  gitlab    │   │  cost      │   │  slack    │
        │  github    │   │  policy    │   │  jira     │
        │  git       │   │  summary   │   │  vault    │
        │            │   │  tfupdate  │   │  ...      │
        └────────────┘   └────────────┘   └───────────┘

Plugins are compiled into the binary. There is no runtime plugin loading — this means zero overhead and full type safety.

Guides

CLI Command Plugin

Add a new terraci <command>. The most common plugin type — perfect for notifications, reports, integrations.

Pipeline Step Plugin

Inject custom jobs or steps into generated CI pipelines. Use for security scans, approval gates, or post-deploy hooks.

CI Provider Plugin

Add support for a new CI system. Implement pipeline generation, environment detection, and MR/PR comments.

Init Wizard Plugin

Add configuration fields to the terraci init interactive wizard. Users configure your plugin through a TUI form.

Built-in Plugins

PluginCapabilitiesConfig
gitChangeDetection, PreflightAlways active
gitlabGenerator, EnvDetector, Comments, Preflight, Initconfig/gitlab
githubGenerator, EnvDetector, Comments, Preflight, Initconfig/github
summaryCommand, Pipeline, InitEnabled by default
costCommand, Pipeline, Runtime, Preflight, Initconfig/cost
policyCommand, Pipeline, Runtime, Preflight, Version, Initconfig/policy
tfupdateCommand, Pipeline, Runtime, Preflight, Initconfig/tfupdate

Plugin Basics

Registration

Every plugin registers itself in init():

go
package myplugin

import (
    "github.com/edelwud/terraci/pkg/plugin"
    "github.com/edelwud/terraci/pkg/plugin/registry"
)

func init() {
    registry.Register(&Plugin{
        BasePlugin: plugin.BasePlugin[*Config]{
            PluginName: "myplugin",
            PluginDesc: "What my plugin does",
            EnableMode: plugin.EnabledExplicitly,
            DefaultCfg: func() *Config { return &Config{} },
            IsEnabledFn: func(cfg *Config) bool {
                return cfg != nil && cfg.Enabled
            },
        },
    })
}

type Plugin struct {
    plugin.BasePlugin[*Config]
}

type Config struct {
    Enabled bool   `yaml:"enabled"`
}

Users configure your plugin in .terraci.yaml:

yaml
plugins:
  myplugin:
    enabled: true

Activation Policies

PolicyBehaviorUsed by
EnabledAlwaysAlways active, no config neededgit
EnabledWhenConfiguredActive when config section existsgitlab, github
EnabledByDefaultActive unless enabled: falsesummary
EnabledExplicitlyRequires explicit opt-incost, policy, update

Lifecycle

Register → Configure → Preflight → Freeze → Execute
  1. Registerinit() runs at import time
  2. Configure — framework decodes plugins.<key> from YAML
  3. Preflight — cheap validation (no network, no heavy state)
  4. Freeze — context becomes immutable
  5. Execute — commands lazily build runtime as needed

AppContext

Every capability receives *plugin.AppContext with:

go
ctx.WorkDir()    // project root directory
ctx.ServiceDir() // resolved .terraci directory (absolute path)
ctx.Config()     // full TerraCi configuration (defensive copy)
ctx.Version()    // TerraCi version string
ctx.Reports()    // shared report registry for plugin communication

Building

bash
# From published module
xterraci build --with github.com/your-org/terraci-plugin-slack

# From local directory during development
xterraci build --with github.com/your-org/plugin=./my-plugin

# Exclude built-in plugins you don't need
xterraci build --without cost --without policy

# Pin specific version
xterraci build --with github.com/your-org/plugin@v1.2.0

See Also

Released under the MIT License.