Skip to content
Connect an agent

Infrastructure as code

Terraform in organisations

How a platform team runs Plotwork for a whole organisation from Terraform or OpenTofu: one workspace owns the brand, the custom icons and the approved patterns, and every application stack publishes its own diagrams to the private library. It is for platform engineers, architects and the owners of an organisation's Plotwork setup.

For
  • GitHub Actions

TL;DR

QuestionAnswer
What can Terraform manage?The organisation's brand colours, custom icons and approved patterns, and every diagram a stack publishes to the library. Members, invitations, keys and settings stay in the portal.
Which key?An organisation key from Keys and CI, never a person's key. Give the design workspace a key with Brand, icons and patterns (design:write) and each application stack a key with Diagrams.
Who can grant design:write?The organisation's owner or an admin. A key without it is refused with orgs.forbidden.
How many workspaces?One design workspace for the organisation, with its own state, and one workspace per application stack for that stack's diagrams.
How does CI get the key?As a sensitive environment variable, PLOTWORK_API_KEY: a GitHub Actions secret, an Azure Pipelines secret variable, or an HCP Terraform or Atlantis variable set. Never in a committed .tfvars file.
What happens to a change made in the portal?The next plan shows it as drift, and the next apply puts the configuration back.
Can I take over what is already there?Yes. terraform import and import blocks take the ids <organisation> for the brand and <organisation>/<name> for icons and patterns.
Which plan?Brand themes and custom icons need the owner on Pro or Enterprise. Approved patterns need Enterprise. Publishing diagrams works on every plan; plotwork_import and plotwork_render need Pro or above.
Is the provider stable?Not yet. Only beta releases exist; pin the exact version. See Release channels.

1. The shape of the setup

The platform team keeps the organisation's look in one root module, reviewed like the rest of the platform. Application teams keep their diagrams beside their own infrastructure and publish them to the library. Neither needs the other's key.

WorkspaceOwnsKey scopeState
Design, one for the organisationplotwork_organization_brand, plotwork_organization_icon, plotwork_patternBrand, icons and patterns (design:write)Its own. Nothing else writes these resources.
An application stack, one for eachplotwork_diagram, with plotwork_import or templatefile()DiagramsThe stack's own, beside its infrastructure

Keep the design resources in exactly one state. Two workspaces that both declare the brand, or the same icon name, undo each other's apply in turn.

2. Make the keys

  1. In the portal, open the organisation and choose Keys and CI.
  2. Create a key named for where it runs, such as terraform-design, and tick Brand, icons and patterns.
  3. Create one key for each application pipeline, such as terraform-payments, with Diagrams ticked.
  4. Copy each key into the secret store of the pipeline that uses it (section 5). An owner or admin can reveal a key again later.
ScopeLets the keyGive it to
DiagramsRead the library, publish, move, rename and delete diagramsApplication stacks
Brand, icons and patterns (design:write)Read the organisation, and manage its brand, custom icons and approved patternsThe design workspace
Audit log only (audit:read)Read and export the audit logA SIEM, never Terraform
Drift (drift:write)Report drift runs from CI and read the style rulesThe drift check of the GitHub Action or the Azure Pipelines task

An organisation key belongs to the organisation, not to the person who made it, so it keeps working when that person leaves. It acts only in its own organisation. The organisation holds at most ten active keys. A key belongs to one environment: a key made on the preview portal works with the beta provider's default API, and a production key needs api_url = "https://api.plotwork.dev".

3. Lay out the modules

text
platform-design/              # the design workspace, one per organisation  versions.tf  main.tf                     # brand, icons, patterns  icons/    payments-api.svg    ledger.svg  patterns/    payments-service.pattern.json
payments/infra/               # an application stack  versions.tf  main.tf                     # the stack's own resources  diagrams.tf                 # plotwork_import and plotwork_diagram  network.diagram.tftpl

The design workspace:

HCL
terraform {  required_providers {    plotwork = {      source  = "plotwork-official/plotwork"      version = "0.1.0-beta.2"    }  }}
# Reads PLOTWORK_API_KEY: the design key.provider "plotwork" {  organization = "acme"}
resource "plotwork_organization_brand" "this" {  primary = "#2a78d6"}
resource "plotwork_organization_icon" "this" {  for_each = fileset("${path.module}/icons", "*.svg")
  name = trimsuffix(each.value, ".svg")  svg  = file("${path.module}/icons/${each.value}")}
resource "plotwork_pattern" "this" {  for_each = fileset("${path.module}/patterns", "*.pattern.json")
  content = file("${path.module}/patterns/${each.value}")}
output "icon_refs" {  value = { for file, icon in plotwork_organization_icon.this : file => icon.ref }}

An application stack:

HCL
# Reads PLOTWORK_API_KEY: this stack's diagrams key.provider "plotwork" {  organization = "acme"}
data "plotwork_import" "infra" {  format = "terraform"  files  = { for f in fileset(path.module, "*.tf") : f => file("${path.module}/${f}") }}
resource "plotwork_diagram" "infra" {  source         = data.plotwork_import.infra.source  name           = "Payments infrastructure"  folder         = "Payments/Infrastructure"  classification = "internal"}

A stack draws the organisation's icons by name, as icon=org:payments-api, in a hand-written template. It needs no reference to the design workspace's state: the name is the contract. Hosting custom icons for Terraform covers where the SVGs can live.

4. Names, folders and labels

SettingConventionWhy
folder<Team>/<Area>, outermost first, such as Payments/InfrastructureMembers browse the library by folder, and a folder rule can restrict who sees it. A restricted folder takes a key only when the rule names key.
nameWhat a reader looks for, such as Payments infrastructure. Keep it fixed.The library lists diagrams by name. Removing name later keeps the old label.
classificationSet it on every diagram: internal by default, confidential for anything that names security controls or data flows of personal dataLeft out, the folder's or the organisation's default applies. A confidential diagram is never shared at a public link.
Icon nameLower-case words joined by hyphens, after the service, such as payments-apiIt is what diagrams write after org:. Changing it replaces the icon, and every diagram that names the old one draws a placeholder.
Icon aliasesThe words people write in labels, such as payments serviceSearch and automatic icon matching find the icon by them.
aliasNot for an organisation's diagramA stable link is a public address in your own library. An organisation's diagram has none; members open it with view_url, which changes when the source does.

5. Run it in CI

Set the key as the environment variable PLOTWORK_API_KEY, marked sensitive, in the system that runs the plan. Leave api_key out of the configuration, and never write a key in a .tfvars file that is committed. If a module takes the key as a variable, declare it sensitive = true.

GitHub Actions

yaml
name: plotwork-design
on:  pull_request:  push:    branches: [main]
permissions:  contents: read
jobs:  terraform:    runs-on: ubuntu-latest    env:      PLOTWORK_API_KEY: ${{ secrets.PLOTWORK_DESIGN_KEY }}      PLOTWORK_ORG: acme    steps:      - uses: actions/checkout@v7      - uses: hashicorp/setup-terraform@v4      - run: terraform init      - run: terraform plan -out=tfplan      - if: github.ref == 'refs/heads/main'        run: terraform apply tfplan

For OpenTofu, use opentofu/setup-opentofu@v2 and tofu in place of terraform. Keep the secret in a protected environment when pull requests from forks can run the workflow.

HCP Terraform, Terraform Enterprise and Atlantis

RunnerWhere the key goes
HCP Terraform or Terraform EnterpriseA variable set with an environment variable PLOTWORK_API_KEY, marked Sensitive, applied to the design workspace only. A second variable set with a diagrams key, applied to the application workspaces.
AtlantisThe server's environment, or a secret the server reads into PLOTWORK_API_KEY. Give the design repository its own server or workflow when it must not share a key with application repositories.
Spacelift, env0, ScalrThe same: an environment variable marked secret, scoped to the stacks that need it.

6. Take over what is already there

An organisation that set its brand, icons or patterns in the portal can move them into code without deleting anything. Write the resources, then import each one:

HCL
import {  to = plotwork_organization_brand.this  id = "acme"}
import {  to = plotwork_organization_icon.this["payments-api.svg"]  id = "acme/payments-api"}
import {  to = plotwork_pattern.this["payments-service.pattern.json"]  id = "acme/payments-service"}
ResourceImport id
plotwork_organization_brand<organisation>, its slug or id
plotwork_organization_icon<organisation>/<name>
plotwork_pattern<organisation>/<name>, the pattern's name
plotwork_diagram<organisation>/<id>, or the id alone for a personal diagram

The command form works too: terraform import 'plotwork_organization_icon.this["payments-api.svg"]' acme/payments-api, or tofu import. Plotwork keeps a checked copy of an icon, not the file, so the first apply after importing an icon uploads the configured file once. Download the icons from the portal first if the repository does not have them yet.

7. Drift: the configuration wins

Terraform reads each resource on every plan. A change made outside it shows as drift, and the next apply puts the configuration back.

Changed in the portalNext planNext apply
A brand colourAn update to the brandSaves the configured colours
An icon replacedAn update: content_sha256 is emptyUploads the configured file again
An icon or pattern deletedA createSaves it again
A pattern editedAn updateSaves the configured file
A diagram deleted, or expired by the planA createPublishes it again
An icon or pattern added in the portalNothingNothing: Terraform manages only what it declares

Tell admins that the design workspace owns the brand, icons and patterns, so a fix belongs in a pull request. On Pro and Enterprise the organisation's audit log records who saved or deleted each icon and pattern.

8. What each plan includes

Resource or data sourceFreeProEnterprise
plotwork_diagramYesYesYes
plotwork_import, plotwork_renderNo, plan.rest_not_includedYesYes
plotwork_organization_brandNo, plan.brand_not_includedYesYes
plotwork_organization_iconNo, plan.icons_not_includedUp to 100 iconsCustom
plotwork_patternNoNo, plan.governance_not_includedUp to 50 patterns

An organisation has its owner's plan, and an organisation key is held to it. If the owner moves to Free, the brand and icons are kept but not drawn, and active on the brand turns false.

9. Best practices

PracticeWhy
One design workspace per organisationThe brand, each icon and each pattern have exactly one owner, so two applies never fight.
A separate key for each pipeline, named for itYou can revoke one pipeline without touching the others, and the call history names it.
design:write only on the design keyAn application pipeline that leaks its key cannot change the organisation's look.
Keys as sensitive environment variablesThey never reach the state, a plan file or a log.
Pin the exact provider version and commit .terraform.lock.hclEvery run uses the same build, and a new beta is a reviewed change.
Review icon SVGs and pattern files in pull requestsThey are what every team's diagrams draw. Run check_pattern on a pattern file before merging.
Send configuration, not state, to plotwork_importState can hold secrets.
Set classification on every organisation diagramThe label decides how far a diagram may travel.

10. Troubleshooting

ErrorCauseFix
401 auth.invalidThe key is wrong or revoked, or it was made in the other environmentUse a key from the portal of the API the provider talks to, or set api_url. A beta talks to preview by default.
403 orgs.forbiddenThe key lacks the scope the resource needs: a design resource without design:write, or a diagram without Diagrams. A personal key of a member who is not an owner or admin.Make a key with the right scope under Keys and CI.
403 orgs.key_wrong_orgAn organisation key was used with no organisation, so the call went to a personal librarySet organization on the provider, or PLOTWORK_ORG.
404 not_foundThe organisation's slug is wrong, or the key belongs to another organisationCheck organization against the organisation's handle.
403 plan.rest_not_includedplotwork_import or plotwork_render with an owner on FreeUpgrade the owner to Pro, or publish with plotwork_diagram from a hand-written source.
403 plan.brand_not_included, plan.icons_not_included, plan.governance_not_includedThe owner's plan does not include brand themes, custom icons or approved patternsSee section 8.
403 orgs.icon_limit_reached, orgs.pattern_limit_reachedThe organisation holds as many as its plan allowsRemove some, or move the owner up a plan.
403 orgs.folder_restrictedThe folder is restricted and its rule does not name keyAsk the owner to add key to the folder rule, or publish to another folder.
422 icon.invalidThe SVG holds something the check refusesdetails lists every reason. See Hosting custom icons for Terraform.
422 orgs.pattern_invalidA template in the pattern file has a mistakeRun check_pattern and fix each problem it names.

See also

PageWhy
Terraform providerInstall, release channels, and every block
Hosting custom icons for TerraformWhere to keep icon SVGs and how the provider reads them
OrganisationsRoles, keys and the private library
Custom iconsWhat an icon may hold and how diagrams draw it
Approved patternsThe pattern file plotwork_pattern reads
Brand themesHow the brand colours paint a diagram
Classification labels and publish approvalWhat each label allows
Enterprise rolloutDiagrams as code across many repositories