Using Harness with Pulumi
Harness is a software delivery platform whose CI module runs pipelines defined as stages of steps. A pipeline can live in Harness itself or in your Git repository through Git Experience.
You run Pulumi in Harness with standard Run steps that invoke the Pulumi CLI. Because the CLI drives every operation, this works with a Pulumi program written in any supported language and targeting any cloud provider Pulumi supports.
This guide assumes Pulumi Cloud as your backend. Pulumi Cloud isn't required to run Pulumi in CI/CD — Pulumi also supports self-managed backends — but the access token, OIDC, and ESC features described here are specific to Pulumi Cloud.
Prerequisites
Before you begin, make sure you have:
- A Pulumi Cloud account and organization.
- A Harness account with the CI module enabled and a project connected to your Git repository.
- A Pulumi program committed to that repository. If you don’t have one yet, follow a Get started guide.
Authenticate with Pulumi Cloud
When your pipeline uses Pulumi Cloud as its backend, it needs only a single Pulumi access token to operate. Add the token as a Harness secret and reference it from a step’s environment variables — the examples below read it from a secret named pulumi_access_token into PULUMI_ACCESS_TOKEN. Prefer an organization or team token over a personal token so the pipeline’s identity isn’t tied to an individual.
Pulumi ESC (Environments, Secrets, and Configuration) then supplies cloud credentials, secrets, and configuration to your Pulumi program. Because ESC delivers those values the same way whether the consumer is a Harness pipeline or a developer’s machine, a single environment definition works in both places — you don’t store separate cloud provider keys as Harness secrets.
To remove the static token entirely, Harness can act as an OIDC issuer, and Pulumi Cloud can register any third-party issuer as a trusted OIDC Issuer. A pipeline then exchanges a short-lived OIDC token for a Pulumi access token at runtime instead of storing one. See OIDC Issuers for the Pulumi Cloud side of the setup, and the Harness documentation for issuing OIDC tokens from a pipeline.
Build a trunk-based CI/CD workflow
The most common way to run Pulumi in CI/CD follows a trunk-based development model: work merges into a single main branch, and deployments flow outward from there.
The pipeline below maps each stage of that model to its own CI stage, gated by a when condition on the build type:
- Open a pull request. The
previewstage runs on pull requests and reports the proposed changes withpulumi preview. - Merge to the main branch. The
deploy-stagingstage runs on pushes tomainand deploys the change to a staging environment withpulumi up. - Promote to production. Pushing a
release-*tag runs thedeploy-productionstage, which deploys to production.
Each Run step uses the official pulumi/pulumi container image, which bundles the Pulumi CLI and every language runtime, so the same step works regardless of your program’s language. Pin the image to a specific version, as the examples do, so builds stay reproducible. The examples assume a Pulumi program in an infra/ directory and stacks named acme/website/staging and acme/website/production:
pipeline:
name: Pulumi
identifier: pulumi
projectIdentifier: my_project
orgIdentifier: default
properties:
ci:
codebase:
connectorRef: my_git_connector
repoName: website
build: <+input>
stages:
# Pull request: preview the proposed changes.
- stage:
name: Preview
identifier: preview
type: CI
spec:
cloneCodebase: true
platform:
os: Linux
arch: Amd64
runtime:
type: Cloud
spec: {}
execution:
steps:
- step:
type: Run
name: Pulumi preview
identifier: pulumi_preview
spec:
shell: Bash
image: pulumi/pulumi:3.242.0
envVariables:
PULUMI_ACCESS_TOKEN: <+secrets.getValue("pulumi_access_token")>
command: |-
pulumi login
pulumi install --cwd infra/
pulumi preview --cwd infra/ --stack acme/website/staging
when:
pipelineStatus: Success
condition: <+codebase.build.type> == "PR"
# Merge to main: deploy to the staging environment.
- stage:
name: Deploy staging
identifier: deploy_staging
type: CI
spec:
cloneCodebase: true
platform:
os: Linux
arch: Amd64
runtime:
type: Cloud
spec: {}
execution:
steps:
- step:
type: Run
name: Pulumi up
identifier: pulumi_up_staging
spec:
shell: Bash
image: pulumi/pulumi:3.242.0
envVariables:
PULUMI_ACCESS_TOKEN: <+secrets.getValue("pulumi_access_token")>
command: |-
pulumi login
pulumi install --cwd infra/
pulumi up --yes --cwd infra/ --stack acme/website/staging
when:
pipelineStatus: Success
condition: <+codebase.build.type> == "branch" && <+codebase.branch> == "main"
# Release tag: promote to production.
- stage:
name: Deploy production
identifier: deploy_production
type: CI
spec:
cloneCodebase: true
platform:
os: Linux
arch: Amd64
runtime:
type: Cloud
spec: {}
execution:
steps:
- step:
type: Run
name: Pulumi up
identifier: pulumi_up_production
spec:
shell: Bash
image: pulumi/pulumi:3.242.0
envVariables:
PULUMI_ACCESS_TOKEN: <+secrets.getValue("pulumi_access_token")>
command: |-
pulumi login
pulumi install --cwd infra/
pulumi up --yes --cwd infra/ --stack acme/website/production
when:
pipelineStatus: Success
condition: <+codebase.build.type> == "tag" && <+codebase.tag>.startsWith("release-")
pulumi install installs the program’s language dependencies and required plugins, and pulumi up --yes applies changes non-interactively, with no confirmation prompt.
To run this pipeline automatically, add two Harness triggers on your repository: a pull request trigger and a push trigger. The push trigger fires for both branch pushes and tag pushes; the per-stage when conditions then route each event to the correct stage. To promote a release, push a tag that matches the release-* pattern:
git tag release-2026-05-20
git push origin release-2026-05-20
Keeping production on its own stack and deploying it only from a tag makes each production update a single, traceable Git operation, and ensures production never deploys from an untested commit. To require human sign-off before a production deployment, add a HarnessApproval step ahead of the pulumi up step in the Deploy production stage.
For an optional ephemeral environment on each pull request, pair the preview stage with a Review Stack, which provisions and tears down a per-pull-request environment automatically.
pulumi/actions as a pipeline step.Speed up builds with caching
A clean CI worker starts with an empty plugin cache, so Pulumi re-downloads its provider plugins on every run. Cache the Pulumi plugin directory (~/.pulumi/plugins) to avoid that.
On Harness Cloud, enable Cache Intelligence on the stage and add the plugin directory to its cached paths:
spec:
cloneCodebase: true
caching:
enabled: true
paths:
- "~/.pulumi/plugins"
On self-managed build infrastructure, use the Save Cache/Restore Cache steps to store the same directory in an S3 or GCS bucket.
The most deterministic option is to bake the plugins into a custom builder image: derive it from pulumi/pulumi, pulumi plugin install the providers your program uses, and reference that image from the Run step instead of pulumi/pulumi. See the plugins documentation for details.
Report results on pull requests
When the preview stage runs pulumi preview on a pull request, you’ll want the proposed changes summarized on the pull request itself rather than buried in the pipeline logs.
Pulumi offers native version control integrations for popular version control systems. Independently of which CI/CD system runs Pulumi, a version control integration lets Pulumi Cloud post infrastructure-change summaries as pull request comments and status checks, and link each stack update back to the commit and pull request that produced it. See the Version Control page for the current list of supported systems.
Managing Harness with Pulumi
You can manage Harness itself — projects, environments, services, connectors, and pipelines — as code with the Pulumi Harness provider in the Pulumi Registry. This lets you define the Harness resources this guide describes as part of a Pulumi program, in any supported language.
Additional resources
- Continuous delivery — overview of running Pulumi in CI/CD.
- Pulumi ESC — deliver credentials, secrets, and configuration to pipelines and developers consistently.
- OIDC Issuers — exchange a CI/CD system’s OIDC token for a short-lived Pulumi access token.
- Version Control — post infrastructure-change summaries on pull requests.
- Review Stacks — ephemeral per-pull-request environments.
- CI/CD troubleshooting — fixes for common failures when running Pulumi in CI/CD.
Thank you for your feedback!
If you have a question about how to use Pulumi, reach out in Community Slack.
Open an issue on GitHub to report a problem or suggest an improvement.