Stacks
Every Pulumi program is deployed to a stack. A stack is an isolated, independently configurable
instance of a Pulumi program. Stacks are commonly used to denote different phases of development (such as development
, staging
, and production
) or feature branches (such as feature-x-dev
).
A project can have as many stacks as you need. By default, Pulumi creates a stack for you when you start a new project using the pulumi new
command.
Create a stack
To create a new stack, use pulumi stack init stackName
. This creates an empty stack stackName
and sets it as the active stack. The project that the stack is associated with is determined by finding the nearest Pulumi.yaml
file.
The stack name must be unique within a project. Stack names may only contain alphanumeric characters, hyphens, underscores, or periods.
$ pulumi stack init staging
If you are using Pulumi in your organization, by default the stack will be created in your user account. To target the organization, name the stack using orgName/stackName
:
$ pulumi stack init broomllc/staging
Fully qualified stack names also include the project name, in the form orgName/projectName/stackName
, and this fully-qualified format is required in some contexts. In most contexts, the shorthands orgName/stackName
or stackName
are valid and use the default organization and the current project context.
Pulumi.<stack-name>.yaml
files, these files are not created by pulumi stack init
. They are created and managed with pulumi config
. For information on how to populate your stack configuration files, see Configuration.Deploy a project
To deploy your project to the currently selected stack, run pulumi up
. The operation uses the latest configuration values for the active stack.
preview
and update
operations by using pulumi.runtime.isDryRun().Listing stacks
To see the list of stacks associated with the current project (the nearest Pulumi.yaml
file), use pulumi stack ls
.
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT
jane-dev 4 hours ago 97
staging* n/a n/a
test 2 weeks ago 121
Select a stack
The top-level pulumi
operations config
, preview
, update
and destroy
operate on the active stack. To change the active stack, run pulumi stack select
.
$ pulumi stack select jane-dev
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT
jane-dev* 4 hours ago 97
staging n/a n/a
test 2 weeks ago 121
To select a stack that is part of an organization, use the fully-qualified stack name, either orgName/stackName
or orgName/projectName/stackName
:
$ pulumi stack select mycompany/prod
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT
mycompany/prod* 4 hours ago 97
mycompany/staging 4 hours ago 97
dev n/a n/a
View stack resources
To view details of the currently selected stack, run pulumi stack
with no arguments. This displays the metadata, resources and output properties associated with the stack.
$ pulumi stack
Current stack is jane-dev:
Last updated 1 week ago (2018-03-02 10:26:09.850357 -0800 PST)
Pulumi version v0.11.0
Plugin nodejs [language] version 0.11.0
Plugin aws [resource] version 0.11.0
Current stack resources (3):
TYPE NAME
pulumi:pulumi:Stack webserver-jane-dev
aws:ec2/securityGroup:SecurityGroup web-secgrp
aws:ec2/instance:Instance web-server-www
Current stack outputs (2):
OUTPUT VALUE
publicDns ec2-18-218-85-197.us-east-2.compute.amazonaws.com
publicIp 18.218.85.197
Use `pulumi stack select` to change stack; `pulumi stack ls` lists known ones
Stack tags
Stacks have associated metadata in the form of tags, with each tag consisting of a name and value. A set of built-in tags are automatically assigned and updated each time a stack is updated (such as pulumi:project
, pulumi:runtime
, pulumi:description
, gitHub:owner
, gitHub:repo
, vcs:owner
, vcs:repo
, and vcs:kind
). To view a stack’s tags, run pulumi stack tag ls
.
Custom tags can be assigned to a stack by running pulumi stack tag set <name> <value>
and can be used to customize the grouping of stacks in the Pulumi Console. For example, if you have many projects with separate stacks for production, staging, and testing environments, it may be useful to group stacks by environment instead of by project. To do this, you could assign a custom tag named environment
to each stack. For example, running pulumi stack tag set environment production
assigns a custom environment
tag with a value of production
to the active stack. Once you’ve assigned an environment
tag to each stack, you’ll be able to group by Tag: environment
in the Pulumi Console.
As a best practice, custom tags should not be prefixed with pulumi:
, gitHub:
, or vcs:
to avoid conflicting with built-in tags that are assigned and updated with fresh values each time a stack is updated.
Tags can be deleted by running pulumi stack tag rm <name>
.
Stack Outputs
A stack can export values as stack outputs. These outputs are shown during an update, can be easily retrieved with the Pulumi CLI, and are displayed in the Pulumi Console. They can be used for important values like resource IDs, computed IP addresses, and DNS names.
To export values from a stack, use the following definition in the top-level of the entrypoint for your project:
exports.url = resource.url;
export let url = resource.url;
pulumi.export("url", resource.url)
ctx.Export("url", resource.Url)
public class MyStack : Stack
{
public MyStack()
{
...
this.Url = resource.Url;
}
// 'url' is the output name. By default, it would take the property name 'Url'.
[Output("url")] Output<string> Url { get; set; }
}
From the CLI, you can then use pulumi stack output url
to get the value and incorporate into other scripts or tools.
The value of a stack export can be a regular value, an Output, or a Promise
(effectively, the same as an Input). The actual values are resolved after pulumi up
completes.
Stack exports are effectively JSON serialized, though quotes are removed when exporting strings.
For example, the following statements:
exports.x = "hello"
exports.o = {num: 42}
export let x = "hello";
export let o = {num: 42};
pulumi.export("x", "hello")
pulumi.export("o", {'num': 42})
ctx.Export("x", pulumi.String("hello"))
ctx.Export("o", pulumi.Map(map[string]pulumi.Input{
"num": pulumi.Int(42),
}))
class MyStack : Stack
{
[Output] public Output<string> x { get; set; }
[Output] public Output<ImmutableDictionary<string, int>> o { get; set; }
public MyStack()
{
this.x = Output.Create("hello");
this.o = Output.Create(
new Dictionary<string, int> { { "num", 42 } }
.ToImmutableDictionary());
}
}
produce the following stack outputs:
$ pulumi stack output x
hello
$ pulumi stack output o
{"num": 42}
The full set of outputs can be rendered as JSON by using pulumi stack output --json
:
$ pulumi stack output --json
{
"x": "hello",
"o": {
"num": 42
}
}
Stack outputs respect secret annotations and are encrypted appropriately. If a stack contains any secret values, their plaintext values will not be shown by default. Instead, they will be displayed as secret in the CLI. Pass --show-secrets
to pulumi stack output
to see the plaintext value.
Getting the Current Stack Programmatically
The getStack
getStack
get_stack
context.Stack
Deployment.StackName
let stack = pulumi.getStack();
let stack = pulumi.getStack();
stack = pulumi.get_stack()
stack := ctx.Stack()
var stack = Deployment.Instance.StackName;
Stack References
Stack references allow you to access the outputs of one stack from another stack. Inter-Stack Dependencies allow one stack to reference the outputs of another stack.
To reference values from another stack, create an instance of the StackReference
type using the fully qualified name of the stack as an input, and then read exported stack outputs by their name:
const pulumi = require("@pulumi/pulumi");
const other = new pulumi.StackReference("acmecorp/infra/other");
const otherOutput = other.getOutput("x");
import * as pulumi from "@pulumi/pulumi";
const other = new pulumi.StackReference("acmecorp/infra/other");
const otherOutput = other.getOutput("x");
from pulumi import StackReference
other = StackReference(f"acmecorp/infra/other")
other_output = other.get_output("x");
other, err := pulumi.NewStackReference(ctx, "acmecorp/infra/other", nil)
if err != nil {
return err
}
otherOutput := other.GetOutput(pulumi.String("x"))
var other = new StackReference("acmecorp/infra/other");
var otherOutput = other.GetOutput("x");
Stack names must be fully qualified, including the organization, project, and stack name components, in the format <organization>/<project>/<stack>
. For individual accounts, use your account name for the organization component.
To expand on this further, imagine you need to define a cluster’s infrastructure in one project and consume it from another.
Perhaps one project, infra
, defines a Kubernetes cluster and another, services
, deploys
services into it. Let’s further imagine you are doing this across three distinct environments: production, staging,
and testing. In that case, you will have six distinct stacks that pair up in the following ways:
mycompany/infra/production
provides the cluster used bymycompany/services/production
mycompany/infra/staging
provides the cluster used bymycompany/services/staging
mycompany/infra/testing
provides the cluster used bymycompany/services/testing
The way Pulumi programs communicate information for external consumption is by using stack exports. For example, your infrastructure stack might export the Kubernetes configuration information needed to deploy into a cluster:
exports.kubeConfig = ... a cluster's output property ...;
export const kubeConfig = ... a cluster's output property ...;
pulumi.export("kubeConfig", ... a cluster's output property ...)
ctx.Export("kubeConfig", /*...a cluster's output property...*/)
class ClusterStack : Stack
{
[Output] public Output<string> KubeConfig { get; set; }
public ClusterStack()
{
// ... a cluster is created ...
this.KubeConfig = ... a cluster's output property ...
}
}
The challenge in this scenario is that the services project needs to ingest this output during deployment so that it can connect to the Kubernetes cluster provisioned in its respective environment.
The Pulumi programming model offers a way to do this with its StackReference
resource type. For example:
const k8s = require("@pulumi/kubernetes");
const pulumi = require("@pulumi/pulumi");
const env = pulumi.getStack();
const infra = new pulumi.StackReference(`mycompany/infra/${env}`);
const provider = new k8s.Provider("k8s", { kubeconfig: infra.getOutput("kubeConfig") });
const service = new k8s.core.v1.Service(..., { provider: provider });
import * as k8s from "@pulumi/kubernetes";
import * as pulumi from "@pulumi/pulumi";
const env = pulumi.getStack();
const infra = new pulumi.StackReference(`mycompany/infra/${env}`);
const provider = new k8s.Provider("k8s", { kubeconfig: infra.getOutput("kubeConfig") });
const service = new k8s.core.v1.Service(..., { provider: provider });
from pulumi import get_stack, ResourceOptions, StackReference
from pulumi_kubernetes import Provider, core
env = get_stack()
infra = StackReference(f"mycompany/infra/{env}")
provider = Provider("k8s", kubeconfig=infra.get_output("kubeConfig"))
service = core.v1.Service(..., ResourceOptions(provider=provider))
import (
"fmt"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
slug := fmt.Sprintf("mycompany/infra/%v", ctx.Stack())
stackRef, err := pulumi.NewStackReference(ctx, slug, nil)
kubeConfig := stackRef.GetOutput(pulumi.String("kubeConfig"))
// ...
return nil
}
}
using Pulumi;
using Pulumi.Kubernetes.Core.V1;
class AppStack : Stack
{
public AppStack()
{
var cluster = new StackReference($"mycompany/infra/{Deployment.Instance.StackName}");
var kubeConfig = cluster.RequireOutput("KubeConfig").Apply(v => v.ToString());
var provider = new Provider("k8s", new ProviderArgs { KubeConfig = kubeConfig });
var options = new ComponentResourceOptions { Provider = provider };
var service = new Service(..., ..., options);
}
}
The StackReference
constructor takes as input a string of the form <organization>/<project>/<stack>
, and lets
you access the outputs of that stack.
In the above example, you construct a stack reference to a specific stack in this project which has the same name
as your current stack (i.e. when deploying the “staging” stack of the above program, you reference the “staging” stack)
from the infra project. Once you have that resource, you can fetch the kubeConfig
output variable with the getOutput
function. From that point onwards, Pulumi understands the inter-stack dependency for scenarios like cascading updates.
Import and export a stack deployment
A stack can be exported to see the raw data associated with the stack. This is useful when manual changes need to be applied to the stack due to changes made in the target cloud platform that Pulumi is not aware of. The modified stack can then be imported to set the current state of the stack to the new values.
$ pulumi stack export --file stack.json
$ pulumi stack import --file stack.json
Destroy a stack
Before deleting a stack, if the stack still resources associated with it, they must first be deleted via pulumi destroy
. This command uses the latest configuration values, rather than the ones that were last used when the program was deployed.
Delete a stack
To delete a stack with no resources, run pulumi stack rm
. Removing the stack will remove all stack history from pulumi.com and will delete the stack configuration file Pulumi.<stack-name>.yaml
.
To force the deletion of a stack that still contains resources—potentially orphaning them—use pulumi stack rm --force
.