Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions generated/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
Expand Down
16 changes: 14 additions & 2 deletions pages/plural-features/stacks-iac-management/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:

Expand Down
118 changes: 118 additions & 0 deletions pages/plural-features/stacks-iac-management/pulumi.md
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions src/routing/docs-structure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Comment on lines 165 to +166

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Navigation order puts advanced topic before setup page

customize-runners appears immediately before pulumi in the sidebar. The Pulumi stacks page itself links readers to customize-runners for workload identity setup, making it a follow-on reference — so new users browsing the sidebar top-to-bottom would reach the advanced runner-customization page before discovering how to create a Pulumi stack at all. Moving pulumi to appear before customize-runners would match the natural progression from "create a stack" to "tune how it runs."

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

{ path: 'pr-workflow', title: 'Stack PR workflow' },
{ path: 'manual-runs', title: 'Manual runs' },
{
Expand Down
Loading