terraform-provider-stackgen

StackGen Provider

The StackGen provider manages projects, members, roles, secrets, and AI agents on the StackGen platform. All services (AppCD, Vault, Guild) are accessed via a single host URL with path-based routing.

StackGen Documentation

Finding documentation: The repository README.md explains how to use this docs/ tree, AGENTS.md (sg struct tags), and terraform providers schema -json / tofu providers schema -json for machine-readable schema. The generated Schema section below includes the full provider block documentation (including adopt_on_conflict).

Example Usage

# StackGen Basic Example
#
# Creates a project and assigns the current user as admin (uses sg_me and sg_roles).
#
# Usage:
#   cd examples/basic
#   tofu init
#   tofu plan
#   tofu apply

# ---------------------------------------------------------------------------
# Provider
# ---------------------------------------------------------------------------

terraform {
  required_providers {
    sg = {
      source = "releases.stackgen.com/stackgen/stackgen"
    }
  }
}

provider "sg" {
  stackgen_url   = var.stackgen.stackgen_url
  stackgen_token = var.stackgen.stackgen_token
  # Optional: default UI workspace for agents, knowledge bases, documents, webhooks, remote runners, workflows, etc.
  workspace_id = var.stackgen.stackgen_workspace_id != "" ? var.stackgen.stackgen_workspace_id : null
}

# ---------------------------------------------------------------------------
# Data Sources – current user and roles
# ---------------------------------------------------------------------------

data "sg_me" "current" {}

data "sg_roles" "all" {}

locals {
  admin_role_id = [for r in data.sg_roles.all.roles : r.id if r.name == "admin"][0]
}

# ---------------------------------------------------------------------------
# Project
# ---------------------------------------------------------------------------

resource "sg_project" "demo" {
  name        = "demo-project"
  description = "Demo project created by the basic example"
}

# ---------------------------------------------------------------------------
# Assign current user as admin
# ---------------------------------------------------------------------------

resource "sg_project_member" "admin" {
  project_id = sg_project.demo.id
  user_id    = data.sg_me.current.id
  role_id    = local.admin_role_id
}

Composition order and decision guide

Example snippets under examples/ are illustrative. Replace UUIDs, hostnames, Slack IDs, tokens, and secret values with your own — never commit real credentials.

Compose Guild resources in layers so dependencies exist before consumers reference them by name or ID:

  1. Layer 0 — foundation: sg_project / access, sg_secret / sg_credential_provider, sg_guild_model_provider → sg_guild_model, sg_policy (+ optional sg_policy_bundle)
  2. Layer 1 — integrations & platforms: sg_guild_integration, sg_guild_platform, sg_remote_runner (+ optional sg_remote_runner_secrets), sg_skill_source / sg_skill, knowledge bases / sources / documents
  3. Layer 2 — agents & playbooks: sg_agent (+ budgets, policy attachments), sg_workflow (stages / bindings / runbook refs), sg_runbook_sop, sg_evidence_checklist, sg_remediation_pattern
  4. Layer 3 — triggers & app bindings: sg_webhook, sg_agent_schedule, sg_notification_channel, chat channel / rule / subscription, sg_app → sg_sre_alert_webhook, sg_il_reflection_config
flowchart TD
  modelProvider[sg_guild_model_provider] --> model[sg_guild_model]
  model --> agent[sg_agent]
  policy[sg_policy] --> attach[sg_agent_policy_attachment]
  attach --> agent
  secret[sg_secret] --> integration[sg_guild_integration]
  cred[sg_credential_provider] --> integration
  integration --> agent
  integration --> knowledgeSource[sg_knowledge_source]
  kb[sg_knowledge_base] --> knowledgeSource
  runner[sg_remote_runner] --> runnerSecrets[sg_remote_runner_secrets]
  runner --> agent
  agent --> workflow[sg_workflow]
  runbook[sg_runbook_sop] --> workflow
  workflow --> webhook[sg_webhook]
  workflow --> schedule[sg_agent_schedule]
  agent --> webhook
  agent --> schedule
  notify[sg_notification_channel] --> webhook
  notify --> schedule
  integration --> app[sg_app]
  app --> sreWebhook[sg_sre_alert_webhook]

I want to… → use this resource

Goal Use Notes
Register an LLM endpoint and model name sg_guild_model_provider then sg_guild_model Agents reference model names, not provider IDs
Give an agent tools (Grafana, AWS, MCP, …) sg_guild_integration then set sg_agent.integrations Or data.sg_guild_integration for pre-provisioned names
OAuth instead of static secrets sg_credential_provider → credential_provider_id on the integration Register Vault’s callback URL with the IdP
Govern tool calls with Rego sg_policy + sg_agent_policy_attachment Attach by policy UUID
Multi-stage RCA / remediation playbook sg_workflow (+ sg_runbook_sop) Prefer over a single agent when stages/triggers matter
HTTP trigger for an agent/workflow sg_webhook Not for SRE Copilot alert ingest
SRE Copilot alert ingest (Datadog/Grafana) sg_app then sg_sre_alert_webhook App must already be installed; needs workspace_id
Cron prompt against an agent/workflow sg_agent_schedule Optional notification_channel_ids
Run tools in a private network sg_remote_runner (+ sg_remote_runner_secrets) Attach runner name on sg_agent.remote_runners
Sync wiki/docs into retrieval sg_knowledge_base + sg_knowledge_source One-off URL → sg_knowledge_document
Slack/Teams mentions → agent sg_guild_platform → chat channel/rule → sg_chat_subscription Tags link channels to subscriptions
Adopt an app-owned agent read-only data.sg_agent Do not recreate SRE investigator unless you intend to own it

Set provider workspace_id whenever Guild APIs need workspace scope (agents, apps, knowledge, webhooks, runners, workflows, schedules).

Schema

Optional

Operational notes

Guild resources and data sources are experimental; API shapes and lifecycle behavior may change. Registering sg_guild_integration alone does not attach it: use its name in sg_agent.integrations or sg_app.integrations (for an installed catalog app). Confirm supported integration type values and required image configuration against the target Guild deployment; the provider schema examples are not a guarantee that every backend supports those combinations.

sg_secret metadata can contain actual credentials (for example AWS access keys). sensitive hides values in routine CLI output but does not encrypt or remove them from Terraform state. Protect state and plan files with encrypted storage, restricted access and retention policies. The StackGen Vault stores the remote secret; it is not a substitute for securing local Terraform state. TF_VAR_<name> is a Terraform convention for setting declared input variables (for example TF_VAR_stackgen_token); there is no provider-specific native environment variable fallback for stackgen_url / stackgen_token. Alternatively wire variables explicitly in HCL.

Naming glossary: provider workspace_id is the default UI workspace scope. The top-level UI organization is not necessarily that workspace. An HCL resource label (e.g. sg_agent.example) is Terraform-local, while an agent/integration name is its Guild lookup key and a server id is a separate UUID. Use the identifier required by each reference, not these interchangeably.

Import support

Importing sets Terraform state from a remote read; it does not push your HCL onto the object. After import, inspect tofu plan before applying changes. Adoption on Create is different and should not be used to discover existing config.

Status Resources
Supported (36) All registered resources. See each resource page for the import ID.
Not supported (0) —

Notable import IDs:

Resource Import ID
sg_agent / sg_agent_budget agent name
sg_agent_policy_attachment <agent_name>/<policy_id>
sg_credential_provider provider name (supply client_id/client_secret in HCL after import)
sg_guild_model / sg_guild_model_provider / sg_policy_bundle name
sg_knowledge_document document_id or document_id\|source_url
sg_webhook webhook UUID
sg_workflow workflow name, or UUID (resolved via ListWorkflows; there is still no get-by-UUID API)
sg_secret UUID or name
sg_project_member <project_id>/<user_id>
sg_governance governance UUID (tenant), or <owner_type>/<owner_id>/<governance_id>
sg_governance_assignment <governance_version_id>/<owner_type>/<workspace_id>

Consult the individual resource Import section and verify the first refresh succeeds.

FAQ

How do I take over an object created in the UI without changing it?

Check the import matrix and the resource’s Import section. Import by that resource’s key, then review tofu plan and reconcile HCL before tofu apply. Workflow import accepts a name or a UUID (UUID is resolved by listing workflows in the workspace, then state stores the name). Prefer document_id|source_url when importing sg_knowledge_document. adopt_on_conflict is not a read/diff workflow: it only adopts on verified Create conflicts when configured fields match the remote object, and may still leave a plan to reconcile. Set it to false when you do not want any supported conflict adoption. A server 500 or timeout is ambiguous; inspect remote state before retrying.

I created an integration; why does the UI still show nothing under “Attach to”?

The integration resource registers the connection. Attach its name using sg_agent.integrations or sg_app.integrations (catalog app bindings). sg_guild_integration.secret_ref_ids refers to separately managed sg_secret UUIDs; credentials are not configured on an agent. Every integration Create checks the scoped name before upsert. If it already exists, only an exact configuration match can be adopted when enabled; a mismatch fails without overwriting it. A concurrent create can still race without server-side conditional creation. The integration data source returns image, environment, and secret references if Guild supplies them; these values may be sensitive. See sg_guild_integration, sg_agent, and sg_app.

Where are my credentials, and will sensitive keep them out of state?

The UI calls them integration Credentials, but Terraform manages Vault secret values in sg_secret.metadata; integrations reference those secret IDs via secret_ref_ids. Backend auto-created secrets such as <integration>-token remain Vault-owned; manage or import them as sg_secret only when you intend Terraform to own them. metadata is marked sensitive to mask routine CLI output, not to omit it from plan/state files. Secure remote state and access to plan artifacts.

Discover per-type keys with sg_vault_secret_metadata_keys. For AWS assume-role, sg_secret.metadata.aws_role_arn is the customer role to assume, while data.sg_vault_aws_config.bastion_role_arn is the StackGen principal used in its trust policy; do not swap them. Vault may redact values on read: after import or refresh the provider warns when aws_role_arn is missing from the Vault response so you can pin it in HCL before apply. Vault PUT metadata semantics (replace vs append duplicate keys) should be confirmed against the Vault service before rotating overlapping keys. For GCP, see the GCP impersonation section and examples/resources/sg_secret/resource_gcp.tf. See also sg_secret and sg_vault_aws_config.

Can I inventory everything before writing resources?

Use sg_secrets, sg_credential_providers, sg_guild_integrations, sg_guild_integration_types (type/image matrix from the Guild registry), sg_knowledge_bases, sg_knowledge_documents, and sg_knowledge_sources. Singular lookups exist for credential providers, knowledge documents, and knowledge sources. Never assume a missing data source means the object does not exist.

What if a knowledge-base request returns 500 but the object exists?

Do not blindly retry. On an ambiguous Create failure (HTTP 5xx / empty response), sg_knowledge_base attempts a verified recovery: list by name, require a unique match whose managed fields equal the plan, then GET by UUID before writing state. Unverified 500s are not adopted. Listing can also return a transient backend 500; record trace ID, workspace scope, time, and the redacted response for the StackGen backend team.

Why do UI, API, and Terraform names differ? Which provider name goes in HCL?

UI / conversation term Terraform / provider term
Workspace (project picker) Provider workspace_id (preferred); deprecated project_id / org_id still fall back
Organization (tenant admin) Not the same as workspace_id; do not assume 1:1
Credentials (integration attach) sg_secret.metadata + sg_guild_integration.secret_ref_ids
Guild / Aiden APIs Resources and data sources that call Guild OpenAPI
Local provider name in HCL Always sg when you declare required_providers { sg = { source = "releases.stackgen.com/stackgen/stackgen" } }

The resource prefix is sg_, while the registry source is releases.stackgen.com/stackgen/stackgen. If generated config says provider = stackgen, change it to the declared local provider name sg before applying.

Are there native token/URL environment variables? Are the examples or old releases interchangeable?

No provider-native STACKGEN_* URL/token environment fallback is implemented. Declare Terraform input variables and pass TF_VAR_... to them, or configure HCL explicitly; TF_TOKEN_releases_stackgen_com authenticates the registry, not the StackGen API. Use the examples with the current provider schema; do not set read-only attributes such as sg_agent.environment/contact_id or sg_remediation_pattern.version, and do not use removed knowledge.graph_enabled (removed in v0.1.38; pins such as 0.1.42 reject it).

When does an Experimental Guild resource graduate?

Resources marked Experimental stay experimental until all of the following are true for a release:

  1. Upstream OpenAPI for that resource is stable enough that a regenerated client does not force a breaking schema change in the same minor line without a changelog Migration note.
  2. The resource supports import and has unit coverage for create/read/update/delete (or documented no-op delete) plus a successful refresh after import.
  3. Ownership is documented: which fields Terraform manages vs server-owned (for example auto-created <integration>-token secrets).
  4. The Experimental banner is removed from the resource MarkdownDescription and the changelog records the graduation.

Until then, pin a provider version and expect attribute churn on Guild-backed types.

GCP impersonation setup

Vault GCP secrets (category = "CloudProvider", subcategory = "gcp") use metadata keys from sg_vault_secret_metadata_keys (filter subcategory = "gcp"). Typical flows store a service-account JSON or impersonation target in sg_secret.metadata.

When StackGen must impersonate a customer GCP service account, grant the StackGen bastion service account roles/iam.serviceAccountTokenCreator on that customer SA. The bastion principal is deployment-specific: ask your StackGen operator for the exact email for your environment (it is not hard-coded in this provider). Then reference the Vault secret from sg_guild_integration.secret_ref_ids. See examples/resources/sg_secret/resource_gcp.tf.

Resources

Projects & Access Control

Secrets

AI Agents

Governance & Policies

Knowledge & Runbooks

Models & Integrations

Workflows & Automation

Chat

Apps & SRE Copilot

Remote runners

Data Sources

AppCD

Vault

Guild