Programming Model

In Pulumi, resources are defined by allocating resource objects in a program, such as new aws.ec2.Instance(...). The first argument passed to the resource constructor is its name, which must be unique within the Pulumi program. To create dependencies between resources, just reference the output properties of a resource. For example, this definition of an EC2 instance creates a dependency on a SecurityGroup:

let group = new aws.ec2.SecurityGroup(...);

let server = new aws.ec2.Instance("webserver-www", {
    ...
    securityGroups: [ group.name ], // reference the security group resource above
});

To publish values that you wish to access outside your application, create a stack output via module exports.

In Pulumi, you can group multiple resources in a component. A component is a logical container for physical cloud resources and affects how resources are grouped in the CLI and the pulumi.com console.

Programs

Pulumi programs are authored in JavaScript or Python. You can use any packages supported by the languages package manager, as well as Pulumi packages.

When pulumi update is run, your Pulumi program is run in either Node.js or Python and the Pulumi CLI determines the desired state of application resources. A Pulumi program can reference artifacts that have already been published (such as S3 objects or pre-built Docker images) or it can define application resources itself so that everything is versioned together. For example, if your program uses cloud.Service with a build step, or defines a Lambda for an S3 trigger, you’re defining application code that is implicitly deployed during the pulumi update.

A Pulumi program is contained within a project. In JavaScript, the main property of package.json defines the entry point for the Pulumi program.

@pulumi/pulumi Package

The @pulumi/pulumi package is the core library for working with the Pulumi planning engine. This package defines the following:

Dependencies between resources are encoded with pulumi.Output.

This package also provides the following helpers:

Creating resources

A resource is created via new Resource(name) in JavaScript. All resources must have a name, which must be unique in the Pulumi program.

All resource constructors take the following additional properties.

  • dependencies - a list of explicit resource dependencies
  • protect - whether to mark a resource as protected. A protected resource cannot be deleted directly: first you must set protect: false and run pulumi update. Then, the resource can be deleted, either by removing the line of code or by running pulumi destroy.
  • parent - Optional parent for the resource. See Components.

Resource outputs

The outputs of resource objects have type Output. Resource inputs take either a raw value or an output from another resource. To transform an output into a new value, use the apply method.

For example, use the following to create an HTTPS URL from the DNS name of a virtual machine:

virtualmachine.dnsName.apply(dnsName => "https://" + dnsName)

Stack output

A stack output is a value that can be easily retrieved from the Pulumi CLI and is displayed on pulumi.com. To export value from a stack, use the following definition in the top-level of the entry point for your project:

JavaScript

exports.url = ...

TypeScript

export let url = ...

Python

pulumi.output(url, ...)

From the CLI, you can then use pulumi stack output url to get the value and incorporate into other scripts or tools.

The right-hand side of a stack export can be a regular JavaScript value, an [Output], or a Promise. The actual value will be resolved at the end of pulumi update.

Stack exports are JSON serialized, though quotes are removed when exporting just a string value. For example:

exports.x = "hello" 
// result of `pulumi stack output x`:
// hello

exports.o = {num: 42}
// result of `pulumi stack output o`:
// {"num": 42}

Using configuration values

To access configuration values that have been set with pulumi config set, use the following:

let config = new pulumi.Config("broome-proj"); // broome-proj is name defined in Pulumi.yaml
console.log(`Hello, ${config.require("name")}!`);	    // prints "BroomeLLC"

In the Pulumi CLI, configuration values are always created as string values. But, you can extract a strongly-typed form with methods such as config.getNumber, config.getBoolean, and so on.

Components

A Pulumi component is a logical group of resources which contains other components and physical cloud resources. A Pulumi stack is itself a component that contains all top-level components and resources in a program.

To create a new component, either in a top-level program or in a library, create a subclass of pulumi.ComponentResource. Components provide a way to create reusable abstractions made up of other resources.

Here’s a simple component definition:

class MyResource extends pulumi.ComponentResource {
    constructor(name, opts) {
        super("pkg:MyResource", name, {}, opts);
    }
}

The call to super registers the component instance with the Pulumi engine. This records the resource in the checkpoint and tracks it across program deployments. Since all resources must have a name, a component constructor should accept a name and pass it to super.

A component must register a namespace, such as pkg:MyResource in the example above. To reduce the potential of name conflicts, this name should contain the package name and resource type, such as aws:lambda:Function.

Components will often contain child resources. To track this relationship, pass the component instance as the parent when constructing a resource:

let bucket = new aws.s3.Bucket(`${name}-bucket`, {}, { parent: this });

Components can define their own properties using registerOutputs. The Pulumi engine uses this information to track dependencies between resources.

this.registerOutputs({
    bucketDnsName: bucket.bucketDomainName,
})

For more information about components, see the Pulumi Components tutorial.

Packages

Pulumi packages are normal NPM or Python packages. They transitively depend on @pulumi/pulumi which defines how resources created by a Pulumi program will be communicated to the Pulumi engine. This ability to register resources with the Pulumi engine is the only difference between a Pulumi package and any other NPM package.

Some Pulumi packages have a dependency on a Resource Provider plugin which contains the implementation for how to Create, Read, Update and Delete resources defined by the package. The pulumi.CustomResource base class is used to connect a JavaScript resource class with the resource provider it depends on for resource management. Packages like @pulumi/aws and @pulumi/kubernetes define resources, such as aws.ec2.Intance, kubernetes.Pod, which are managed by the AWS and Kubernetes resource providers.

A CustomResource needs an associated CRUD provider, whereas a ComponentResource does not — its logic is authored entirely in JavaScript in Python. Packages such as @pulumi/cloud and @pulumi/aws-infra contain only these higher-level component resources.

Runtime code

You can create a component that allows the caller to pass in runtime JavaScript functions. For example, a JavaScript callback could be used as the implementation of an AWS Lambda function. This is enabled by the pulumi.runtime.serializeFunctionAsync API, which takes as input a JavaScript Function object, and returns a Promise<string> that contains the serialized form of that function.

The serialized form is a module with a single exported function named handler which is a function with the same signature as the inputs.

When serializing a function to text, the following steps are taken:

  1. Any captured variables referenced by the function are evaluated when the function is serialized.
  2. The values of those variables are serialized.
  3. When the values are objects, all properties and prototype chains are serialized. When the values are functions, those functions are serialized by following these same steps.