Stack READMEs in the Pulumi Service

Posted on

Starting today, users can create Stack READMEs in the Pulumi Service that dynamically update based on Stack Outputs.

Each Pulumi Stack you deploy manages a key set of cloud infrastructure for your organization. The Pulumi Console includes a variety of features for exposing key information about your stack for other users within your organization - configuration, outputs, resources under management, links to cloud providers, and a graph of all resources. However, it’s often useful to allow the author of a Pulumi Stack to describe in their own words the key elements of a stack, so future viewers can quickly understand the components and cloud resources that are managed.

A README is a text file that introduces and explains a project. A Pulumi Service Stack README can contain documentation and common links for the Stack. The key difference between a source control README, such as a GitHub README, and a Pulumi Service Stack README, is that it is dynamically populated with details from your stack outputs. It does this by interpolating output variables on the stack (${outputs.instances[0].ARN}) so that each stack can construct links to dashboards, shell commands, and other pieces of documentation. All of this content stays up to date as you stand up new stacks, rename resources, and refactor your infrastructure.

There are a lot of operational activities that happen around Pulumi Stacks, but critical information is spread across many tools. We have heard from our users that it can be time consuming switching from different tools and web apps to manage their deployments- from their cloud Console, to the Pulumi Service User Interface (UI), to the CLI, and so on. By collecting resource outputs in the Pulumi Service UI, we are reducing this friction and keeping relevant Stack information in one place so that is kept up to date automatically.

README templates can reference Stack outputs and resource properties, for example ${outputs.vpc.ARN}. To walk you through a tangible example, a user can deploy an RDS instance and then use a variable in their README template to link to their CloudWatch dashboard to keep an eye on operational metrics. This CloudWatch dashboard link will dynamically update when deployments happen. You can use the same README template across dev, testing and production and have the correct dashboard links for each Stack. Learn more in the Stack READMEs documentation page.

The new experience lives in the Stack page, which can be navigated to through Projects and clicking on the specific Stack you want to view the README for. Let’s take a look at this new feature!

Stack READMEs in the Pulumi Console

The Pulumi.README.md file we added to the Stack ‘Production’ above is a template README for our Pulumi Service. In it we have links to our AWS authentication tool, our CloudWatch metrics, to our production deployment documentation, and so on. The common places that we navigate to and from when managing this Stack. Here is some of the Markdown for this README:

# Pulumi Service README

[Sign in to AWS to view stack resources!](https://top-secret-url.com)
โ€‹
## On Call Operations
โ€‹
### Monitor
โ€‹
**Cloudwatch Metrics**
Monitor holistic metrics tracking overall service health
[Link](https://us-west-2.console.aws.amazon.com/cloudwatch/home?region=us-west-2#dashboards:name=${outputs.dashboardName})

**RDS Performance Metrics**
Monitor RDS performance (wait times, top queries)
[Link](https://us-west-2.console.aws.amazon.com/rds/home?region=us-west-2#performance-insights-v20206:/resourceId/${outputs.databaseClusterId}/resourceName/${outputs.rdsClusterWriterInstance})

**Cloudwatch Logs**
Search across service logs
[Link](https://us-west-2.console.aws.amazon.com/cloudwatch/home?region=us-west-2#logStream:group=${outputs.cloudwatchLogGroup})

How to add a Stack README to your Stack

In order to add a README to your Pulumi Stack, you will need to do the following:

Step 1

Export a Stack output named readme that contains your templated Stack README markdown, commonly by reading a file, i.e. Pulumi.README.md.

import { readFileSync } from "fs";
export const strVar = "foo";
export const arrVar = ["fizz", "buzz"];
// add readme to stack outputs. must be named "readme".
export const readme = readFileSync("./Pulumi.README.md").toString();
import pulumi
pulumi.export('strVar', 'foo')
pulumi.export('arrVar', ['fizz', 'buzz'])
# open template readme and read contents into stack output
with open('./Pulumi.README.md') as f:
    pulumi.export('readme', f.read())
func main() {
  pulumi.Run(func(ctx *pulumi.Context) error {
    strVar := "foo"
    arrVar := []string{"fizz", "buzz"}
    readmeBytes, err := ioutil.ReadFile("./Pulumi.README.md")
    if err != nil {
      return fmt.Errorf("failed to read readme: %w", err)
    }
    ctx.Export("strVar", pulumi.String(strVar))
    ctx.Export("arrVar", pulumi.ToStringArray(arrVar))
    ctx.Export("readme", pulumi.String(string(readmeBytes)))
    return nil
  })
}
using Pulumi;
class MyStack : Stack
{
    public MyStack()
    {
        this.StrVar = "foo";
        this.ArrVar = new string[] { "fizz", "buzz" };
        this.Readme = System.IO.File.ReadAllText("./Pulumi.README.md");
    }
    [Output]
    public Output<string> StrVar { get; set; }
    [Output]
    public Output<string[]> ArrVar { get; set; }
    [Output]
    public Output<string> Readme { get; set; }
}
package stackreadme;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
public class App {
    public static void main(String[] args) {
        Pulumi.run(ctx -> {
            var strVar = "foo";
            var arrVar = new String[]{ "fizz", "buzz" };
            try {
                var readme = Files.readString(Paths.get("./Pulumi.README.md"));
                ctx.export("strVar", Output.of(strVar));
                ctx.export("arrVar", Output.of(arrVar));
                ctx.export("readme", Output.of(readme));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }
}
name: stack-readme-yaml
runtime: yaml
description: A minimal Pulumi YAML program demonstrating stack readme feature
variables:
  readme:
    Fn::ReadFile: ./Pulumi.README.md
outputs:
  strVar: foo
  arrVar:
    - fizz
    - buzz
  readme: ${readme}

Step 2

Create a README template for the Stack. In this example, we will create a Pulumi.README.md file that looks as follows:

# Stack README

Full markdown support! Substitute stack outputs dynamically so that links can depend on your infrastructure! Link to dashboards, logs, metrics, and more.

1. Reference a string stack output: ${outputs.strVar}
2. Reference an array stack output: ${outputs.arrVar[1]}

Step 3

Run pulumi up on that Stack

Step 4

Open the Pulumi Service UI, navigate to Projects and then the Stack you have updated. Once on the Stack page you will see the README tab with your README file.

Ta da! ๐ŸŽ‰

We now have a README on the Stack.

Refer to the Stack README documentation page for more details on how to use this feature. As always, please feel free to submit feature requests and bug reports to the Pulumi Service GitHub Repo. We love hearing feedback from users about ways we can improve your productivity when using Pulumi. We look forward to seeing how you make Stack READMEs fit your needs!