1. Docs
  2. Infrastructure as Code
  3. Concepts
  4. Inputs & outputs

Inputs & outputs

    Pulumi resources use special types to define their properties, called Inputs and Outputs. These special Pulumi types wrap “plain” values like strings or integers, and are what allow Pulumi to declaratively manage your infrastructure resources.

    What are inputs and outputs?

    Pulumi IaC programs use special types called inputs and outputs to keep track of the dependencies between resources. Inputs and outputs, combined with your Pulumi stack’s state file, are what allow Pulumi IaC programs to be declarative. In other words, you only need to tell Pulumi the desired state of your resources, and Pulumi will figure out what needs to be changed, and in what order those operations need to happen, to turn your declared desired state into the actual state of your resources.

    Inputs are values that you can supply to a resource. Inputs may be required or optional: For example, the vpcId input is required on the aws.ec2.Subnet resource because a subnet must belong to a particular VPC. On the other hand, the forceDestroy attribute on an aws.s3.Bucket resource (which allows you to delete a bucket that has objects in it) is optional and defaults to false.

    When specifying inputs to a Pulumi resource, you can always use the plain version of the type. For example, any input that is defined as pulumi.Input<string> will accept a plain string value.

    Outputs are values that are only known after a resource is created. For example, if you are creating an aws.ec2.Vpc resource, the VPC ID is an output - you cannot choose this value, and it is only known after the VPC is created in AWS.

    When authoring Pulumi IaC programs, you will frequently use one resource’s output as another resource’s input. For example, you might create an aws.ec2.Vpc resource and pass its vpcId property (an output) to create several aws.ec2.Subnet resources (where vpcId is a required input). Pulumi uses inputs and outputs to automatically keep track of the dependencies between your resources. Using our example using Pulumi IaC to manage a VPC and its subnets, Pulumi will manage your resources in the following ways:

    • When running pulumi up, Pulumi will ensure that no subnets are created until the VPC has been created and its VPC ID is known. If you are running your Pulumi program for the first time, this means Pulumi will wait until the VPC is created. If you ran your program before and are now adding an additional subnet, that subnet will be created immediately because the VPC ID is already known. (The value is stored in your Pulumi state file.)
    • If you were running a pulumi destroy command to delete all the resources in your program, Pulumi would ensure that the VPC is not deleted until all subnets in your Pulumi program have been deleted.
    Most dependencies between resources are automatically tracked by virtue of one resource’s output being another resource’s input. However, there may be other dependencies between resources that are not defined by this output-to-input relationship. For these cases, you can use the dependsOn resource option to explicitly define a dependency between resources.

    Input and Output types are defined for each supported Pulumi language in the corresponding Pulumi SDK for that language. For example, in TypeScript, the Pulumi Node SDK has definitions for the types pulumi.Input<T> and pulumi.Output<T>. The Pulumi SDK is typically imported by default whenever you create a new Pulumi program, e.g. with pulumi new typescript, pulumi new python, etc.

    Why are inputs and outputs necessary?

    Pulumi inputs and outputs are what allow Pulumi IaC programs to manage your resources declaratively despite being written in imperative general purpose programming languages.

    In imperative programming, you write step-by-step instructions telling the computer exactly how to perform a task, in the exact order those steps must occur. For example, in a traditional imperative program, you might write code that says “first create a VPC, wait for it to be created, get its ID, then create a subnet using that ID.”

    In declarative programming, you describe the desired end state you want to achieve, and the system figures out how to reach that state. For example, in a declarative infrastructure program, you would describe “I want a VPC and a subnet in that VPC,” and the system automatically determines that the VPC must be created first, waits for it to be ready, and then creates the subnet.

    Pulumi’s input and output system enables this declarative approach by automatically tracking dependencies between resources. When you pass an output from one resource as an input to another, Pulumi records that dependency and ensures the resources are created, updated, or destroyed in the correct order—without you needing to write explicit sequencing logic.

    Working with inputs

    All resources in Pulumi accept values that describe the way the resource behaves. These values are called inputs.

    const myId = new random.RandomId("mine", {
        byteLength: 8, // byteLength is an input
    });
    
    const myId = new random.RandomId("mine", {
        byteLength: 8, // byteLength is an input
    });
    
    my_id = random.RandomId("mine",
        byte_length=8 # byte_length is an input
    )
    
    
    myId, err := random.NewRandomId(ctx, "mine", &random.RandomIdArgs{
    	ByteLength: pulumi.Int(8), // ByteLength is an input
    })
    if err != nil {
    	return err
    }
    
    var myId = new Random.RandomId("mine", new()
    {
        ByteLength = 8, // ByteLength is an input
    });
    
    var myId = new RandomId("mine", RandomIdArgs.builder()
        .byteLength(8) // byteLength is an input
        .build());
    
    resources:
      myId:
        type: random:randomId
        properties:
          byteLength: 8 # byteLength is an input
    

    Inputs are generally representations of the parameters to the underlying API call of any resource that Pulumi is managing. The simplest way to create a resource with its required inputs is to use a plain value, like a string:

    const key = new tls.PrivateKey("my-private-key", {
        algorithm: "ECDSA", // ECDSA is a plain value
    });
    
    const key = new tls.PrivateKey("my-private-key", {
        algorithm: "ECDSA", // ECDSA is a plain value
    });
    
    key = tls.PrivateKey("my-private-key",
      algorithm="ECDSA", # ECDSA is a plain value
    )
    
    
    key, err := tls.NewPrivateKey(ctx, "my-private-key", &tls.PrivateKeyArgs{
    	Algorithm:  pulumi.String("ECDSA"), // ECDSA is a plain value
    })
    if err != nil {
    	return err
    }
    
    var key = new PrivateKey("my-private-key", new PrivateKeyArgs{
        Algorithm = "ECDSA", // ECDSA is a plain value
    });
    
    var key = new PrivateKey("my-private-key", PrivateKeyArgs.builder()
        .algorithm("ECDSA") // ECDSA is a plain value
        .build()
    )
    
    resources:
      key:
        type: tls:PrivateKey
        properties:
          algorithm: "ECDSA" # ECDSA is a plain value
    

    Working with outputs

    All resources created by Pulumi will have properties which are returned from the cloud provider API. These values are called outputs.

    This section is about resource outputs, which are related to, but not the same as stack outputs. A stack output is a value that is exported at the end of a successful update, usually intended for use outside of the Pulumi program context: either from the command line via the pulumi stack output command or in another Pulumi program by using a stack reference.

    Outputs are similar to promises or futures: They represent values that are not initially known but will become known once an infrastructure resource has completed provisioning. In other words, outputs represent asynchronous values. Outputs are necessary in Pulumi because provisioning resources is an asynchronous operation: It takes time for a cloud provider to finish provisioning a resource (several minutes in some cases).

    Because outputs represent asynchronous values, they must be handled differently than plain types like string. For example, you cannot directly print the value of an output using your language’s string printing function (e.g. console.log() in TypeScript, print in Python, etc.). Instead, you must use methods supplied in the Pulumi SDK to access the value once it is known.

    The Pulumi SDK provides several basic methods for accessing the plain values of outputs once they are known:

    • Apply allows you to access a single output’s plain value
    • All allows you to access multiple outputs’ plain values

    Both apply and all allow you to return a value, which itself is also a Pulumi output. Transforming output values into other outputs is often useful. For example, you may want to take a DNS name that is the output of a load balancer and transform it into a full URL by appending https://. You can do this using apply.

    In addition to the basic methods apply and all, each Pulumi language’s SDK may also provide helper methods that allow you to work with Outputs similarly to plain types. For example:

    • The Pulumi Node SDK contains a method pulumi.jsonStringify() which mirrors Node’s JSON.stringify() function.
    • The Pulumi Python SDK contains a method pulumi.Output.json_dumps() which mirrors the function dumps() in the standard Python json library.

    Check your language’s Pulumi SDK documentation for a complete listing:

    Using inputs and outputs together

    In Pulumi programs, you will often use one resource’s output as another resource’s input. Pulumi will keep track of this dependency behind the scenes to ensure that your resources are changed in the necessary order:

    let password = new random.RandomPassword("password", {
        length: 16,
        special: true,
        overrideSpecial: "!#$%&*()-_=+[]{}<>:?",
    });
    let example = new aws.rds.Instance("example", {
        instanceClass: "db.t3.micro",
        allocatedStorage: 64,
        engine: "mysql",
        username: "someone",
        password: password.result, // We pass the output from password as an input
    });
    
    const password = new random.RandomPassword("password", {
        length: 16,
        special: true,
        overrideSpecial: "!#$%&*()-_=+[]{}<>:?",
    });
    const example = new aws.rds.Instance("example", {
        instanceClass: "db.t3.micro",
        allocatedStorage: 64,
        engine: "mysql",
        username: "someone",
        password: password.result, // We pass the output from password as an input
    });
    
    password = random.RandomPassword(
        "password",
        length=16,
        special=True,
        override_special="!#$%&*()-_=+[]{}<>:?"
    )
    example = aws.rds.Instance(
        "example",
        instance_class="db.t3.micro",
        allocated_storage=64,
        engine="mysql",
        username="someone",
        password=password.result, # We pass the output from password as an input
    )
    
    password, err := random.NewRandomPassword(ctx, "password", &random.RandomPasswordArgs{
    	Length:          pulumi.Int(16),
    	Special:         pulumi.Bool(true),
    	OverrideSpecial: pulumi.String("!#$%&*()-_=+[]{}<>:?"),
    })
    if err != nil {
    	return err
    }
    _, err = rds.NewInstance(ctx, "example", &rds.InstanceArgs{
    	InstanceClass:    pulumi.String("db.t3.micro"),
    	AllocatedStorage: pulumi.Int(64),
    	Engine:           pulumi.String("mysql"),
    	Username:         pulumi.String("someone"),
    	Password:         password.Result, // We pass the output from password as an input
    })
    if err != nil {
    	return err
    }
    
    var password = new Random.RandomPassword("password", new()
    {
        Length = 16,
        Special = true,
        OverrideSpecial = "!#$%&*()-_=+[]{}<>:?",
    });
    
    var example = new Aws.Rds.Instance("example", new()
    {
        InstanceClass = "db.t3.micro",
        AllocatedStorage = 64,
        Engine = "mysql",
        Username = "someone",
        Password = password.Result, // We pass the output from password as an input
    });
    
    var password = new RandomPassword("password", RandomPasswordArgs.builder()
        .length(16)
        .special(true)
        .overrideSpecial("!#$%&*()-_=+[]{}<>:?")
        .build());
    
    var example = new Instance("example", InstanceArgs.builder()
        .instanceClass("db.t3.micro")
        .allocatedStorage(64)
        .engine("mysql")
        .username("someone")
        .password(password.result()) // We pass the output from password as an input
        .build());
    
      Neo just got smarter about infrastructure policy automation