From f4380176701366ae09541b1f05a5767e81f38ce0 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 14 Jul 2026 15:18:28 +0200 Subject: [PATCH] add Pulumi support in Stacks IaC management and update docs --- generated/routes.json | 8 +- .../stacks-iac-management/index.md | 16 ++- .../stacks-iac-management/pulumi.md | 118 ++++++++++++++++++ src/routing/docs-structure.ts | 1 + 4 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 pages/plural-features/stacks-iac-management/pulumi.md diff --git a/generated/routes.json b/generated/routes.json index 81970d7e..efbf68e5 100644 --- a/generated/routes.json +++ b/generated/routes.json @@ -153,11 +153,11 @@ }, "/plural-features/continuous-deployment/service-templating": { "relPath": "/plural-features/continuous-deployment/service-templating/index.md", - "lastmod": "2026-06-11T11:13:44.082Z" + "lastmod": "2026-07-14T13:15:49.255Z" }, "/plural-features/continuous-deployment/service-templating/supporting-liquid-filters": { "relPath": "/plural-features/continuous-deployment/service-templating/supporting-liquid-filters.md", - "lastmod": "2026-06-11T11:13:44.104Z" + "lastmod": "2026-07-14T13:15:49.275Z" }, "/plural-features/continuous-deployment/lua": { "relPath": "/plural-features/continuous-deployment/lua.md", @@ -207,6 +207,10 @@ "relPath": "/plural-features/stacks-iac-management/customize-runners.md", "lastmod": "2025-03-12T14:59:41.000Z" }, + "/plural-features/stacks-iac-management/pulumi": { + "relPath": "/plural-features/stacks-iac-management/pulumi.md", + "lastmod": "2026-07-14T13:15:49.496Z" + }, "/plural-features/stacks-iac-management/pr-workflow": { "relPath": "/plural-features/stacks-iac-management/pr-workflow.md", "lastmod": "2025-03-12T14:59:41.000Z" diff --git a/pages/plural-features/stacks-iac-management/index.md b/pages/plural-features/stacks-iac-management/index.md index 88b092a3..d040a65d 100644 --- a/pages/plural-features/stacks-iac-management/index.md +++ b/pages/plural-features/stacks-iac-management/index.md @@ -2,7 +2,7 @@ title: Stacks — IaC management description: How to manage Infrastructure as Code at Scale Using Plural --- -The goal of Plural Stacks is to provide a scalable framework to manage infrastructure as code like Terraform, Ansible and Pulumi with a kubernetes-friendly, api-driven approach. The core workflow is as follows: +The goal of Plural Stacks is to provide a scalable framework to manage infrastructure as code with a Kubernetes-friendly, API-driven approach. Stacks support Terraform, Terragrunt, Pulumi, Ansible, and custom runner workflows. The core workflow is as follows: * Declaratively define a stack with a type (terraform, ansible, etc), a location in a git repository to source code from, and a cluster on which it will execute * On each commit to the tracked git repository, a run is created which the Plural deployment operator will detect and execute on the targeted cluster @@ -14,9 +14,21 @@ To get a better idea of the full power of the experience, feel free to take a lo {% embed url="https://youtu.be/06WXbvw6p3w" aspectRatio="16 / 9" /%} +# Supported stack types + +| Type | Use it for | +| --- | --- | +| `TERRAFORM` | Terraform configurations | +| `TERRAGRUNT` | Terraform configurations orchestrated with Terragrunt | +| `PULUMI` | Pulumi programs in supported runtimes | +| `ANSIBLE` | Ansible playbooks | +| `CUSTOM` | A custom runner image and command workflow | + +Pulumi stacks use Pulumi's own state backends. See {% doclink to="plural_features_stacks_iac_management_pulumi" %}Pulumi stacks{% /doclink %} for Pulumi Cloud and self-managed backend authentication. + # A Basic Stack -The most common way to instantiate a stack is via Kubernetes CRD. This gives a flexible, modular way of recreating infrastructure with Terraform and pairs nicely with our PR Automation tooling for full self-service around IaC. +The most common way to instantiate a stack is via Kubernetes CRD. This gives a flexible, modular way to recreate infrastructure and pairs nicely with our PR Automation tooling for full self-service around IaC. Here's an example: diff --git a/pages/plural-features/stacks-iac-management/pulumi.md b/pages/plural-features/stacks-iac-management/pulumi.md new file mode 100644 index 00000000..f2ec6a5e --- /dev/null +++ b/pages/plural-features/stacks-iac-management/pulumi.md @@ -0,0 +1,118 @@ +--- +title: Pulumi stacks +description: Configure Pulumi stacks and authenticate to Pulumi Cloud or self-managed state backends +--- + +Plural runs Pulumi programs through an `InfrastructureStack` with `type: PULUMI`. A Pulumi run logs in to the configured backend, selects or creates the configured stack, then runs the usual preview, apply, or destroy workflow. + +```yaml +apiVersion: deployments.plural.sh/v1alpha1 +kind: InfrastructureStack +metadata: + name: example-pulumi-stack + namespace: default +spec: + type: PULUMI + repositoryRef: + name: infrastructure + namespace: default + clusterRef: + name: mgmt + namespace: infra + git: + ref: main + folder: pulumi + configuration: + pulumi: + stack: dev + parallel: 10 + refresh: true + approveEmpty: true +``` + +`configuration.pulumi.stack` defaults to `dev`. Omit `backendUrl` to use Pulumi Cloud, or set it to a Pulumi-supported self-managed backend URL. Unlike Terraform, `manageState` does not manage Pulumi state; configure the state backend with `backendUrl`. + +## Pulumi Cloud + +Pulumi Cloud is the default backend. Authenticate the runner with a `PULUMI_ACCESS_TOKEN` sourced from a Kubernetes Secret in the same namespace as the `InfrastructureStack`. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: pulumi-cloud-credentials + namespace: default +type: Opaque +stringData: + accessToken: YOUR-PULUMI-ACCESS-TOKEN +--- +apiVersion: deployments.plural.sh/v1alpha1 +kind: InfrastructureStack +metadata: + name: example-pulumi-cloud + namespace: default +spec: + type: PULUMI + # repositoryRef, clusterRef, and git omitted + environment: + - name: PULUMI_ACCESS_TOKEN + secretKeyRef: + name: pulumi-cloud-credentials + key: accessToken +``` + +Create access tokens in Pulumi Cloud for the organization that owns the project and stack. Do not put a token in `backendUrl`, Git, or a plain environment value. + +## S3 and other self-managed backends + +To use S3, set `backendUrl` and authenticate the runner to AWS. Workload identity is recommended: configure the stack runner's service account with EKS Pod Identity or IRSA, then select it with `jobSpec.serviceAccount`. + +```yaml +apiVersion: deployments.plural.sh/v1alpha1 +kind: InfrastructureStack +metadata: + name: example-pulumi-s3 + namespace: default +spec: + type: PULUMI + # repositoryRef, clusterRef, and git omitted + configuration: + pulumi: + stack: dev + backendUrl: s3://YOUR-PULUMI-STATE-BUCKET?region=eu-central-1&awssdk=v2 + environment: + - name: AWS_REGION + value: eu-central-1 + - name: PULUMI_CONFIG_PASSPHRASE + secretKeyRef: + name: pulumi-state-credentials + key: passphrase + jobSpec: + namespace: plrl-deploy-operator + serviceAccount: stacks +``` + +The IAM identity must be allowed to read and write the state bucket. Do not set `AWS_ACCESS_KEY_ID` or `AWS_SECRET_ACCESS_KEY` when the runner has workload identity: static credentials take precedence and can prevent the AWS SDK from using the service account identity. + +Pulumi's default secrets provider for self-managed object-store backends is a passphrase. Store a stable passphrase in a Kubernetes Secret and supply it as `PULUMI_CONFIG_PASSPHRASE`: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: pulumi-state-credentials + namespace: default +type: Opaque +stringData: + passphrase: REPLACE-WITH-A-STABLE-SECRET +``` + +Keep the same passphrase for every run that accesses a stack. Losing or changing it prevents Pulumi from decrypting existing stack configuration and secrets. + +Other Pulumi-supported backends, such as GCS, Azure Blob Storage, and a self-hosted Pulumi Cloud service, are configured the same way: set `backendUrl`, then provide the backend's credentials to the runner through workload identity, a mounted credentials file, or a Secret-backed environment variable. A self-managed backend does not require `PULUMI_ACCESS_TOKEN` unless the selected backend itself requires it. + +{% callout severity="warning" %} +`backendUrl` is normal stack configuration, not a secret field. Do not embed tokens, SAS credentials, or access keys in it. Keep credentials in Kubernetes Secrets or workload identity instead. +{% /callout %} + +See {% doclink to="plural_features_stacks_iac_management_customize_runners" %}Customize stack runners{% /doclink %} for configuring workload identity and runner service accounts. diff --git a/src/routing/docs-structure.ts b/src/routing/docs-structure.ts index 57dfcb72..f9f333b9 100644 --- a/src/routing/docs-structure.ts +++ b/src/routing/docs-structure.ts @@ -163,6 +163,7 @@ export const docsStructure: DocSection[] = [ title: 'Stacks — IaC management', sections: [ { path: 'customize-runners', title: 'Customize stack runners' }, + { path: 'pulumi', title: 'Pulumi stacks' }, { path: 'pr-workflow', title: 'Stack PR workflow' }, { path: 'manual-runs', title: 'Manual runs' }, {