1. Docs
  2. Concepts
  3. Inputs & outputs

Inputs & outputs

    Inputs

    All resources in Pulumi accept values that describe the way the resource behaves. We call these values 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.

    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
    

    Plain value in this document is used to describe a standard string, boolean, integer or other typed value or data structure in your language of choice. Plain value is a way of differentiating these language specific values from Pulumi’s asynchronous values.

    Once an Output Output<T> Output[T] Output Output<T> value has returned (generally from an API) to Pulumi, it can be used as a plain value. For more information, see apply.

    However, in most Pulumi programs, the inputs to a resource will reference values from another resource:

    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());
    

    In this case, Pulumi is taking the output from one resource and using it as the input to another resource.

    Outputs

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

    Outputs are values of type Output Output<T> Output[T] Output Output<T> , which behave very much like promises or monads. This is necessary because outputs are not fully known until the infrastructure resource has actually completed provisioning, which happens asynchronously. Outputs are also how Pulumi tracks dependencies between resources. When an output from one resource has been returned from the cloud provider API, Pulumi can link the two resources together and pass it as the input to another resource.

    Pulumi automatically captures dependencies when you pass an output from one resource as an input to another resource. Capturing these dependencies ensures that the physical infrastructure resources are not created or updated until all their dependencies are available and up-to-date.

    Because outputs are asynchronous, their actual plain values are not immediately available. If you need to access an output’s plain value—for example, to compute a derived, new value, or because you want to log it—you have these options:

    • Apply: a callback that receives the plain value, and computes a new output
    • Lifting: directly read properties off an output value
    • Interpolation: concatenate string outputs with other strings directly

    Apply

    To access the plain (or returned) value of an output, use apply apply Apply Apply . This method accepts a callback that will be invoked with the plain value, once that value is available.

    This example will wait for the value to be returned from the API and print it to stdout

    let myPet = new random.RandomPet("my-pet")
    myPet.id.apply(id => console.log(`Hello, {id}!`))
    
    const myPet = new random.RandomPet("my-pet")
    myPet.id.apply(id => console.log(`Hello, {id}!`))
    
    my_pet = random.RandomPet("my-pet")
    my_pet.id.apply(lambda id: print(f"Hello, {id}!"))
    
    myPet, err := random.NewRandomPet(ctx, "my-pet", &random.RandomPetArgs{})
    if err != nil {
    	return err
    }
    
    myPet.ID().ApplyT(func(id string) error {
        fmt.Printf("Hello, %s!", id)
    	return nil
    })
    
    var myPet = new Pulumi.Random.RandomPet("my-pet", new(){});
    myPet.Id.Apply(id => { Console.WriteLine($"Hello, {id}!"); return id; });
    
    var myId = new RandomPet("my-pet", RandomPetArgs.builder()
        .build());
    
    myId.id().applyValue(i -> {
        System.out.println("Hello," + i + "!");
        return null;
    });
    

    You can use this same process to create new output values to pass as inputs to another resource, for example, the following code creates an HTTPS URL from the DNS name (the plain value) of a virtual machine:

    let url = virtualmachine.dnsName.apply(dnsName => "https://" + dnsName);
    
    let url = virtualmachine.dnsName.apply(dnsName => "https://" + dnsName);
    
    url = virtual_machine.dns_name.apply(
        lambda dns_name: "https://" + dns_name
    )
    
    url := vpc.DnsName.ApplyT(func(dnsName string) string {
        return "https://" + dnsName
    }).(pulumi.StringOutput)
    
    var url = virtualmachine.DnsName.Apply(dnsName => "https://" + dnsName);
    
    var url = virtualmachine.dnsName().applyValue(dnsName -> "https://" + dnsName);
    
    variables:
      url: https://${virtualmachine.DnsName}
    

    The result of the call to apply apply Apply Apply is a new Output. So in this example, the url variable is also an Output Output<T> Output[T] Output Output<T> . It will wait for the new value to be returned from the callback, and carries the dependencies of the original Output. If the callback itself returns an Output, the dependencies of that output are also kept in the resulting Output.

    During some program executions, apply doesn’t run. For example, it won’t run during a preview, when resource output values may be unknown. Therefore, you should avoid side-effects within the callbacks. For this reason, you should not allocate new resources inside of your callbacks either, as it could lead to pulumi preview being wrong.

    All

    If you have multiple outputs and need to use them together, the all function acts like an apply over many resources, allowing you to use multiple outputs when creating a new output. all waits for all output values to become available and then provides them as plain values to the supplied callback. This function can be used to compute an entirely new output value, such as by adding or concatenating outputs from two different resources together, or by creating a new data structure that uses them. Just like with apply, the result of Output.all is itself an Output.

    For example, let’s use a server and a database name to create a database connection string:

    var pulumi = require("@pulumi/pulumi");
    // ...
    let connectionString = pulumi.all([sqlServer.name, database.name])
        .apply(([server, db]) => `Server=tcp:${server}.database.windows.net;initial catalog=${db}...`);
    
    import * as pulumi from "@pulumi/pulumi";
    // ...
    let connectionString = pulumi.all([sqlServer.name, database.name])
        .apply(([server, db]) => `Server=tcp:${server}.database.windows.net;initial catalog=${db}...`);
    

    In python, you can pass in unnamed arguments to Output.all to create an Output list, for example:

    from pulumi import Output
    # ...
    connection_string = Output.all(sql_server.name, database.name) \
        .apply(lambda args: f"Server=tcp:{args[0]}.database.windows.net;initial catalog={args[1]}...")
    

    Or, you can pass in named (keyword) arguments to Output.all to create an Output dictionary, for example:

    from pulumi import Output
    # ...
    connection_string = Output.all(server=sql_server.name, db=database.name) \
        .apply(lambda args: f"Server=tcp:{args['server']}.database.windows.net;initial catalog={args['db']}...")
    
    
    connectionString := pulumi.All(sqlServer.Name, database.Name).ApplyT(
        func (args []interface{}) pulumi.StringOutput  {
            server := args[0]
            db := args[1]
            return pulumi.Sprintf("Server=tcp:%s.database.windows.net;initial catalog=%s...", server, db)
        },
    )
    
    A note on error handling The function ApplyT spawns a Goroutine to await the availability of the implicated dependencies. This function accepts a T or (T, error) signature; the latter accomodates for error handling. Alternatively, one may use the ApplyTWithContext function in which the provided context can be used to reject the output as canceled. Error handling may also be achieved using an error chan.
    // When all the input values have the same type, Output.All can be used and produces an ImmutableArray.
    var connectionString = Output.All(sqlServer.name, database.name)
        .Apply(t => $"Server=tcp:{t[0]}.database.windows.net;initial catalog={t[1]}...");
    
    // For more flexibility, 'Output.Tuple' is used so that each unwrapped value will preserve their distinct type.
    var connectionString2 = Output.Tuple(sqlServer.name, database.name)
        .Apply(t => $"Server=tcp:{t.Item1}.database.windows.net;initial catalog={t.Item2}...");
    
    // Or using a more natural Tuple syntax and a statement lambda expression.
    var connectionString2 = Output.Tuple(sqlServer.name, database.name).Apply(t =>
    {
        var (serverName, databaseName) = t;
        return $"Server=tcp:{serverName}.database.windows.net;initial catalog={databaseName}...";
    });
    
    // When all the input values have the same type, Output.all can be used
    var connectionString = Output.all(sqlServer.name(), database.name())
            .applyValue(t -> String.format("Server=tcp:%s.database.windows.net;initial catalog=%s...", t.get(0), t.get(1));
    
    // For more flexibility, 'Output.tuple' is used so that each unwrapped value will preserve their distinct type.
    var connectionString2 = Output.tuple(sqlServer.name, database.name)
            .applyValue(t -> String.format("Server=tcp:%s.database.windows.net;initial catalog=%s...", t.t1, t.t2));
    
    variables:
      connectionString: Server=tcp:${sqlServer.name}.database.windows.net;initial catalog=${database.name}...
    

    Notice that Output.all works by returning an output that represents the combination of multiple outputs so that, within the callback, the plain values are available inside of a tuple.

    Accessing Nested Properties of an Output by Lifting

    While often, Outputs return asynchronous values that wrap primitive types like strings or integers, sometimes an output has an object with deeply nested values. These properties need to be passed to other inputs as well.

    For example, to read a domain record from an ACM certificate, you need to access the domain validation options, which returns an array. Because that value is an output, we would normally need to use apply apply Apply Apply :

    let certCertificate = new aws.acm.Certificate("cert", {
        domainName: "example.com",
        validationMethod: "DNS",
    });
    let certValidation = new aws.route53.Record("cert_validation", {
        records: [
            // Need to pass along a deep subproperty of this Output
            certCertificate.domainValidationOptions.apply(
                domainValidationOptions => domainValidationOptions[0].resourceRecordValue),
        ],
        ...
    });
    
    let certCertificate = new aws.acm.Certificate("cert", {
        domainName: "example.com",
        validationMethod: "DNS",
    });
    let certValidation = new aws.route53.Record("cert_validation", {
        records: [
            // Need to pass along a deep subproperty of this Output
            certCertificate.domainValidationOptions.apply(
                domainValidationOptions => domainValidationOptions[0].resourceRecordValue),
        ],
        ...
    });
    
    certificate = aws.acm.Certificate('cert',
        domain_name='example.com',
        validation_method='DNS'
    )
    
    record = aws.route53.Record('validation',
        records=[
            # Need to pass along a deep subproperty of this Output
            certificate.domain_validation_options.apply(
                lambda domain_validation_options: domain_validation_options[0]['resourceRecordValue']
            )
        ],
        ...
    )
    
    cert, err := acm.NewCertificate(ctx, "cert", &acm.CertificateArgs{
        DomainName:       pulumi.String("example"),
        ValidationMethod: pulumi.String("DNS"),
    })
    if err != nil {
        return err
    }
    
    record, err := route53.NewRecord(ctx, "validation", &route53.RecordArgs{
        Records: pulumi.StringArray{
            cert.DomainValidationOptions.ApplyT(func(opts []acm.CertificateDomainValidationOption) string {
                return *opts[0].ResourceRecordValue
            }).(pulumi.StringOutput),
        },
        ...
    })
    if err != nil {
        return err
    }
    
    var cert = new Certificate("cert", new CertificateArgs
    {
        DomainName = "example",
        ValidationMethod = "DNS",
    });
    
    var record = new Record("validation", new RecordArgs
    {
        Records = {
            cert.DomainValidationOptions.Apply(opts => opts[0].ResourceRecordValue!)
        },
        ...
    });
    
    var cert = new Certificate("cert",
        CertificateArgs.builder()
            .domainName("example")
            .validationMethod("DNS")
            .build());
    
    var record = new Record("validation",
        RecordArgs.builder()
            .records(
                cert.domainValidationOptions()
                .applyValue(opts -> opts.get(0).resourceRecordValue().get())
                .applyValue(String::valueOf)
                .applyValue(List::of))
            .build());
    

    Instead, to make it easier to access simple property and array elements, an Output Output<T> Output[T] Output Output<T> lifts the properties of the underlying value, behaving very much like an instance of it. Lift allows you to access properties and elements directly from the Output Output<T> Output[T] Output Output<T> itself without needing apply apply Apply Apply . If we return to the above example, we can now simplify it:

    let certCertificate = new aws.acm.Certificate("cert", {
        domainName: "example.com",
        validationMethod: "DNS",
    });
    let certValidation = new aws.route53.Record("cert_validation", {
        records: [
            certCertificate.domainValidationOptions[0].resourceRecordValue
        ],
    ...
    
    let certCertificate = new aws.acm.Certificate("cert", {
        domainName: "example.com",
        validationMethod: "DNS",
    });
    let certValidation = new aws.route53.Record("cert_validation", {
        records: [
            certCertificate.domainValidationOptions[0].resourceRecordValue
        ],
    ...
    
    certificate = aws.acm.Certificate('cert',
        domain_name='example.com',
        validation_method='DNS'
    )
    
    record = aws.route53.Record('validation',
        records=[
            certificate.domain_validation_options[0].resource_record_value
        ],
    ...
    
    cert, err := acm.NewCertificate(ctx, "cert", &acm.CertificateArgs{
        DomainName:       pulumi.String("example"),
        ValidationMethod: pulumi.String("DNS"),
    })
    if err != nil {
        return err
    }
    
    record, err := route53.NewRecord(ctx, "validation", &route53.RecordArgs{
        Records: pulumi.StringArray{
            // Notes:
            // * `Index` looks up an index in an `ArrayOutput` and returns a new `Output`.
            // * Accessor methods like `ResourceRecordValue` lookup properties of a custom struct `Output` and return a new `Output`.
            // * `Elem` dereferences a `PtrOutput` to an `Output`, equivalent to `*`.
            cert.DomainValidationOptions.Index(pulumi.Int(0)).ResourceRecordValue().Elem(),
        },
        ...
    })
    if err != nil {
        return err
    }
    
    var cert = new Certificate("cert", new CertificateArgs
    {
        DomainName = "example",
        ValidationMethod = "DNS",
    });
    
    var record = new Record("validation", new RecordArgs
    {
        // Notes:
        // * `GetAt` looks up an index in an `Output<ImmutableArray<T>>` and returns a new `Output<T>`
        // * There are not yet accessor methods for referencing properties like `ResourceRecordValue` on an `Output<T>` directly,
        //   so the `Apply` is still needed for the property access.
        Records = cert.DomainValidationOptions.GetAt(0).Apply(opt => opt.ResourceRecordValue!),
    });
    
    // Lifting is currently not supported in Java.
    
    resources:
      cert:
        type: aws:acm:Certificate
        properties:
          domainName: example
          validationMethod: DNS
      record:
        type: aws:route53:Record
        properties:
          records:
            # YAML handles inputs and outputs transparently.
            - ${cert.domainValidationOptions[0].resourceRecordValue}
    

    This approach is easier to read and write and does not lose any important dependency information that is needed to properly create and maintain the stack. This approach doesn’t work in all cases, but when it does, it can be a great help.

    In JavaScript and TypeScript, a ‘lifted’ property access on an Output<T> that wraps undefined produces another Output<T> with the undefined value instead of throwing or producing a ‘faulted’ Output<T>. In other words, lifted property accesses behave like the ?. (optional chaining operator) in JavaScript and TypeScript. This behavior makes it much easier to form a chain of property accesses on an Output<T>.

    let certValidation = new aws.route53.Record("cert_validation", {
      records: [certCertificate.domainValidationOptions[0].resourceRecordValue],
    
    // instead of
    
    let certValidation = new aws.route53.Record("cert_validation", {
      records: [certCertificate.apply(cc => cc ? cc.domainValidationOptions : undefined)
                               .apply(dvo => dvo ? dvo[0] : undefined)
                               .apply(o => o ? o.resourceRecordValue : undefined)],
    
    let certValidation = new aws.route53.Record("cert_validation", {
      records: [certCertificate.domainValidationOptions[0].resourceRecordValue],
    
    // instead of
    
    let certValidation = new aws.route53.Record("cert_validation", {
      records: [certCertificate.apply(cc => cc ? cc.domainValidationOptions : undefined)
                               .apply(dvo => dvo ? dvo[0] : undefined)
                               .apply(o => o ? o.resourceRecordValue : undefined)],
    

    Working with Outputs and Strings

    Outputs that return to the engine as strings cannot be used directly in operations such as string concatenation until the output has returned to Pulumi. In these scenarios, you’ll need to wait for the value to return using apply apply Apply Apply .

    For example, say you want to create a URL from hostname and port output values. You can do this using apply and all.

    let hostname = res.hostname;
    let port = res.port;
    
    // Would like to produce a string equivalent to: http://${hostname}:${port}/
    let url = pulumi.all([hostname, port]).
        apply(([hostname, port]) => `http://${hostname}:${port}/`);
    
    let hostname: Output<string>;
    let port: Output<number>;
    
    // Would like to produce a string equivalent to: http://${hostname}:${port}/
    let url = pulumi.all([hostname, port]).
        apply(([hostname, port]) => `http://${hostname}:${port}/`);
    
    hostname: Output[str]
    port: Output[int]
    
    # Would like to produce a string equivalent to: http://${hostname}:${port}/
    url = Output.all(hostname, port).apply(lambda l: f"http://{l[0]}:{l[1]}/")
    
    hostname := pulumi.String("localhost").ToStringOutput()
    port := pulumi.Int(8080).ToIntOutput()
    
    // Would like to produce a string equivalent to: http://${hostname}:${port}/
    url := pulumi.All(hostname, port).ApplyT(func (args []interface{}) string {
        return fmt.Sprintf("http://%s:%d/", args[0], args[1])
    })
    
    Output<string> hostname = // get some Output
    Output<int> port = // get some Output
    
    // Would like to produce a string equivalent to: http://{hostname}:{port}/
    var url = Output.Tuple(hostname, port).Apply(t => $"http://{t.Item1}:{t.Item2}/");
    
    Output<String> hostname = Output.of("localhost"); // get some Output
    Output<Integer> port = Output.of(8080); // get some Output
    
    // Would like to produce a string equivalent to: http://{hostname}:{port}/
    var url = Output.tuple(hostname, port)
            .applyValue(t -> String.format("http://%s:%s/", t.t1, t.t2));
    

    However, this approach is verbose and unwieldy. To make this common task easier, Pulumi exposes interpolation helpers that allow you to create strings that contain outputs. These interpolation methods wrap all and apply with an interface that resembles your language’s native string formatting functions.

    // concat takes a list of args and concatenates all of them into a single output:
    const url1 = pulumi.concat("http://", hostname, ":", port, "/");
    // interpolate takes a JavaScript "template literal" and expands outputs correctly:
    const url2 = pulumi.interpolate `http://${hostname}:${port}/`;
    
    // concat takes a list of args and concatenates all of them into a single output:
    const url1: Output<string> = pulumi.concat("http://", hostname, ":", port, "/");
    // interpolate takes a JavaScript "template literal" and expands outputs correctly:
    const url2: Output<string> = pulumi.interpolate `http://${hostname}:${port}/`;
    
    # concat takes a list of args and concatenates all of them into a single output:
    url = Output.concat("http://", hostname, ":", port, "/")
    # format takes a template string and a list of args or keyword args and formats the string, expanding outputs correctly:
    url2 = Output.format("http://{0}:{1}/", hostname, port);
    
    url := pulumi.Sprintf("http://%s:%d/", hostname, port)
    
    // Format takes a FormattableString and expands outputs correctly:
    var url = Output.Format($"http://{hostname}:{port}/");
    
    // Format takes a FormattableString and expands outputs correctly:
    var url = Output.format("http://%s:%s/", hostname, port);
    
    variables:
      url: https://${hostname}:${port}
    

    You can use string interpolation to export a stack output, provide a dynamically computed string as a new resource argument, or even for diagnostic purposes.

    Working with Outputs and JSON

    Often in the course of working with web technologies, you encounter JavaScript Object Notation (JSON) which is a popular specification for representing data. In many scenarios, you’ll need to embed resource outputs into a JSON string. In these scenarios, you need to first wait for the returned output, then build the JSON string:

    const contentBucket = new aws.s3.Bucket("content-bucket", {
      acl: "private",
      website: {
        indexDocument: "index.html",
        errorDocument: "index.html",
      },
      forceDestroy: true,
    });
    
    const originAccessIdentity = new aws.cloudfront.OriginAccessIdentity(
      "cloudfront",
      {
        comment: pulumi.interpolate`OAI-${contentBucket.bucketDomainName}`,
      }
    );
    
    // apply method
    new aws.s3.BucketPolicy("cloudfront-bucket-policy", {
      bucket: contentBucket.bucket,
      policy: pulumi
        .all([contentBucket.bucket, originAccessIdentity.iamArn])
        .apply(([bucketName, iamArn]) =>
          JSON.stringify({
            Version: "2012-10-17",
            Statement: [
              {
                Sid: "CloudfrontAllow",
                Effect: "Allow",
                Principal: {
                  AWS: iamArn,
                },
                Action: "s3:GetObject",
                Resource: `arn:aws:s3:::${bucketName}/*`,
              },
            ],
          })
        ),
    });
    
    bucket = aws.s3.Bucket(
        "content-bucket",
        acl="private",
        website=aws.s3.BucketWebsiteArgs(
            index_document="index.html", error_document="404.html"
        ),
    )
    
    origin_access_identity = aws.cloudfront.OriginAccessIdentity(
        "cloudfront",
        comment=pulumi.Output.concat("OAI-", bucket.id),
    )
    
    bucket_policy = aws.s3.BucketPolicy(
        "cloudfront-bucket-policy",
        bucket=bucket.bucket,
        policy=pulumi.Output.all(
            cloudfront_iam_arn=origin_access_identity.iam_arn,
            bucket_arn=bucket.arn
        ).apply(
            lambda args: json.dumps(
                {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "CloudfrontAllow",
                            "Effect": "Allow",
                            "Principal": {
                                "AWS": args["cloudfront_iam_arn"],
                            },
                            "Action": "s3:GetObject",
                            "Resource": f"{args['bucket_arn']}/*",
                        }
                    ],
                }
            )
        ),
        opts=pulumi.ResourceOptions(parent=bucket)
    )
    

    The Pulumi Go SDK does not currently support serializing or deserializing maps with unknown values. It is tracked here.

    The following is a simplified example of using pulumi.JSONMarshal in Go.

    bucket, err := s3.NewBucket(ctx, "content-bucket", &s3.BucketArgs{
    	Acl: pulumi.String("private"),
    	Website: &s3.BucketWebsiteArgs{
    		IndexDocument: pulumi.String("index.html"),
    		ErrorDocument: pulumi.String("404.html"),
    	},
    })
    if err != nil {
    	return err
    }
    
    originAccessIdentity, err := cloudfront.NewOriginAccessIdentity(ctx, "cloudfront", &cloudfront.OriginAccessIdentityArgs{
    		Comment: pulumi.Sprintf("OAI-%s", bucket.ID()),
    })
    if err != nil {
    	return err
    }
    
    _, err = s3.NewBucketPolicy(ctx, "cloudfront-bucket-policy", &s3.BucketPolicyArgs{
    	Bucket: bucket.ID(),
    	Policy: pulumi.All(bucket.Arn, originAccessIdentity.IamArn).ApplyT(
    		func(args []interface{}) (pulumi.StringOutput, error) {
    			bucketArn := args[0].(string)
    			iamArn := args[1].(string)
    
    			policy, err := json.Marshal(map[string]interface{}{
    				"Version": "2012-10-17",
    				"Statement": []map[string]interface{}{
    					{
    						"Sid":    "CloudfrontAllow",
    						"Effect": "Allow",
    						"Principal": map[string]interface{}{
    							"AWS": iamArn,
    						},
    						"Action":   "s3:GetObject",
    						"Resource": bucketArn + "/*",
    					},
    				},
    			})
    
    			if err != nil {
    				return pulumi.StringOutput{}, err
    			}
    			return pulumi.String(policy).ToStringOutput(), nil
    		}).(pulumi.StringOutput),
    }, pulumi.Parent(bucket))
    if err != nil {
    	return err
    }
    
    var bucket = new Bucket("content-bucket", new BucketArgs
    {
        Acl = "private",
        Website = new BucketWebsiteArgs
        {
            IndexDocument = "index.html",
            ErrorDocument = "404.html",
        },
    });
    
    var originAccessIdentity = new OriginAccessIdentity("cloudfront", new OriginAccessIdentityArgs
    {
        Comment = Output.Format($"OAI-{bucket.Id}"),
    });
    
    var bucketPolicy = new BucketPolicy("cloudfront-bucket-policy", new BucketPolicyArgs
    {
        Bucket = bucket.Bucket,
        Policy = Output.Tuple(bucket.Arn, originAccessIdentity.IamArn)
        .Apply(t =>
        {
            string bucketArn = t.Item1;
            string cloudfrontIamArn = t.Item2;
    
            var policy = new
            {
                Version = "2012-10-17",
                Statement = new object[]
                {
                    new
                    {
                        Sid = "CloudfrontAllow",
                        Effect = "Allow",
                        Principal = new { AWS = cloudfrontIamArn },
                        Action = "s3:GetObject",
                        Resource = $"{bucketArn}/*",
                    },
                },
            };
    
            return JsonSerializer.Serialize(policy);
        }),
    }, new CustomResourceOptions { Parent = bucket });
    

    This operation is so common, Pulumi provides first-class helper functions for deserializing JSON string outputs into your language’s native objects and serializing your language’s native objects to JSON string outputs. These helper functions are designed to remove the process of manually resolving the output inside a apply apply Apply Apply .

    Converting Outputs to JSON

    You can natively represent the definition of an AWS Step Function State Machine and embed outputs from other resources then convert it to a JSON string.

    const stateMachine = new awsnative.stepfunctions.StateMachine("stateMachine", {
        roleArn: sfnRole.arn,
        stateMachineType: "EXPRESS",
        definitionString: pulumi.jsonStringify({
            "Comment": "A Hello World example of the Amazon States Language using two AWS Lambda Functions",
            "StartAt": "Hello",
            "States": {
                "Hello": {
                    "Type": "Task",
                    "Resource": helloFunction.arn, // Pulumi Resource Output
                    "Next": "World",
                },
                "World": {
                    "Type": "Task",
                    "Resource": worldFunction.arn, // Pulumi Resource Output
                    "End": true,
                },
            },
        })
    });
    
    const stateMachine = new awsnative.stepfunctions.StateMachine("stateMachine", {
        roleArn: sfnRole.arn,
        stateMachineType: "EXPRESS",
        definitionString: pulumi.jsonStringify({
            "Comment": "A Hello World example of the Amazon States Language using two AWS Lambda Functions",
            "StartAt": "Hello",
            "States": {
                "Hello": {
                    "Type": "Task",
                    "Resource": helloFunction.arn, // Pulumi Resource Output
                    "Next": "World",
                },
                "World": {
                    "Type": "Task",
                    "Resource": worldFunction.arn, // Pulumi Resource Output
                    "End": true,
                },
            },
        })
    });
    
    state_machine = aws_native.stepfunctions.StateMachine("stateMachine",
        role_arn=sfn_role.arn,
        state_machine_type="EXPRESS",
        definition_string=pulumi.Output.json_dumps({
            "Comment": "A Hello World example of the Amazon States Language using two AWS Lambda Functions",
            "StartAt": "Hello",
            "States": {
                "Hello": {
                    "Type": "Task",
                    "Resource": hello_function.arn, # Pulumi Resource Output
                    "Next": "World",
                },
                "World": {
                    "Type": "Task",
                    "Resource": world_function.arn, # Pulumi Resource Output
                    "End": True,
                },
            },
        })
    )
    

    The Pulumi Go SDK does not currently support serializing or deserializing maps with unknown values. It is tracked here.

    The following is a simplified example of using pulumi.JSONMarshal in Go.

    pulumi.JSONMarshal(pulumi.ToMapOutput(map[string]pulumi.Output{
        "bool": pulumi.ToOutput(true),
        "int":  pulumi.ToOutput(1),
        "str":  pulumi.ToOutput("hello"),
        "arr": pulumi.ToArrayOutput([]pulumi.Output{
            pulumi.ToOutput(false),
            pulumi.ToOutput(1.0),
            pulumi.ToOutput(""),
            pulumi.ToMapOutput(map[string]pulumi.Output{
                "key": pulumi.ToOutput("value"),
            }),
        }),
        "map": pulumi.ToMapOutput(map[string]pulumi.Output{
            "key": pulumi.ToOutput("value"),
        }),
        // The following functionality is currently unsupported as myResource
        // is an unknown value.
        "unknown": myResource.ApplyT(func(res interface{}) (interface{}, error) {
            return "Hello World!", nil
        }),
    }))
    
    var stateMachine = Pulumi.Output.JsonSerialize(Output.Create(new {
            Comment = "A Hello World example of the Amazon States Language using two AWS Lambda Functions",
            StartAt = "Hello",
            States = new Dictionary<string, object?>{
            ["Hello"] = new {
                Type = "Task",
                Resource = helloFunction.Arn, // Pulumi Resource Output
                Next = "World",
            },
            ["World"] = new {
                Type = "Task",
                Resource = worldFunction.Arn, // Pulumi Resource Output
                End = true,
            },
        },
    }));
    

    Parsing JSON Outputs to Objects

    You can parse a JSON string into an object and then, inside of an Apply, manipulate the object to remove all of the policy statements.

    const jsonIAMPolicy = pulumi.output(`{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "s3:ListAllMyBuckets",
                    "s3:GetBucketLocation"
                ],
                "Resource": "*"
            },
            {
                "Sid": "VisualEditor1",
                "Effect": "Allow",
                "Action": "s3:*",
                "Resource": "arn:aws:s3:::my-bucket"
            }
        ]
    }`);
    
    const policyWithNoStatements = pulumi.jsonParse(jsonIAMPolicy).apply(policy => {
        // delete the policy statements
        policy.Statement = [];
        return policy;
    });
    

    For more details view the NodeJS documentation.

    const jsonIAMPolicy = pulumi.output(`{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "s3:ListAllMyBuckets",
                    "s3:GetBucketLocation"
                ],
                "Resource": "*"
            },
            {
                "Sid": "VisualEditor1",
                "Effect": "Allow",
                "Action": "s3:*",
                "Resource": "arn:aws:s3:::my-bucket"
            }
        ]
    }`);
    
    const policyWithNoStatements: Output<object> = pulumi.jsonParse(jsonIAMPolicy).apply(policy => {
        // delete the policy statements
        policy.Statement = [];
        return policy;
    });
    

    For more details view the NodeJS documentation.

    json_iam_policy = pulumi.Output.from_input('''
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "s3:ListAllMyBuckets",
                    "s3:GetBucketLocation"
                ],
                "Resource": "*"
            },
            {
                "Sid": "VisualEditor1",
                "Effect": "Allow",
                "Action": "s3:*",
                "Resource": "arn:aws:s3:::my-bucket"
            }
        ]
    }
    ''')
    
    def update_policy(policy):
        # delete the policy statements
        policy.update({'Statement': []})
        return policy
    
    policy_with_no_statements = \
        pulumi.Output.json_loads(json_IAM_policy).apply(update_policy)
    

    For more details view the Python documentation.

    jsonIAMPolicy := pulumi.ToOutput(`{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "s3:ListAllMyBuckets",
                    "s3:GetBucketLocation"
                ],
                "Resource": "*"
            },
            {
                "Sid": "VisualEditor1",
                "Effect": "Allow",
                "Action": "s3:*",
                "Resource": "arn:aws:s3:::my-bucket"
            }
        ]
    }`).(pulumi.StringInput)
    
    policyWithNoStatements := pulumi.JSONUnmarshal(jsonIAMPolicy).ApplyT(
        func(v interface{}) (interface{}, error) {
    
            // delete the policy statements
            v.(map[string]interface{})["Statement"] = []pulumi.ArrayOutput{}
            return v, nil
        })
    

    For more details view the Go documentation.

    var jsonIAMPolicy = Output.Create(@"
            {
                ""Version"": ""2012-10-17"",
                ""Statement"": [
                    {
                        ""Sid"": ""VisualEditor0"",
                        ""Effect"": ""Allow"",
                        ""Action"": [
                            ""s3:ListAllMyBuckets"",
                            ""s3:GetBucketLocation""
                        ],
                        ""Resource"": ""*""
                    },
                    {
                        ""Sid"": ""VisualEditor1"",
                        ""Effect"": ""Allow"",
                        ""Action"": [
                            ""s3:*""
                        ],
                        ""Resource"": ""arn:aws:s3:::my-bucket""
                    }
                ]
            }
        ");
    
    var policyWithNoStatements = Pulumi.Output.JsonDeserialize<IAMPolicy>(jsonIAMPolicy).Apply(policy =>
    {
        // delete the policy statements.
        policy.Statement = Pulumi.Output.Create(new List<StatementEntry> { });
        return policy;
    })
    

    For more details view the .NET documentation

    Convert Input to Output through Interpolation

    It is possible to turn an Input Input<T> Input[T] Input Input<T> into an Output Output<T> Output[T] Output Output<T> value. Resource arguments already accept outputs as input values however, in some cases you need to know that a value is definitely an Output Output<T> Output[T] Output Output<T> at runtime. Knowing this can be helpful because, since Input Input<T> Input[T] Input Input<T> values have many possible representations—a plain value, a promise, or an output—you would normally need to handle all possible cases. By first transforming that value into an Output Output<T> Output[T] Output Output<T> , you can treat it uniformly instead.

    For example, this code transforms an Input Input<T> Input[T] Input Input<T> into an Output Output<T> Output[T] Output Output<T> so that it can use the apply function:

    function split(input) {
        let output = pulumi.output(input);
        return output.apply(v => v.split());
    }
    
    function split(input: pulumi.Input<string>): pulumi.Output<string[]> {
        let output = pulumi.output(input);
        return output.apply(v => v.split());
    }
    
    def split(input):
        output = Output.from_input(input)
        return output.apply(lambda v: v.split())
    
    func split(input pulumi.StringInput) pulumi.StringArrayOutput {
        return input.ToStringOutput().ApplyT(func(s string) []string {
            return strings.Split(s, ",")
        }).(pulumi.StringArrayOutput)
    }
    
    Output<string[]> Split(Input<string> input)
    {
        var output = input.ToOutput();
        return output.Apply(v => v.Split(","));
    }
    
    public static Output<List<String>> split(Output<String> str) {
        return str.applyValue(v -> List.of(v.split(",")));
    }
    

    Common Pitfalls

    String Interpolation

    When working with outputs and apply, you may see an error message like so:

    Calling __str__ on an Output[T] is not supported. To get the value of an Output[T] as an Output[str] consider: 1. o.apply(lambda v: f"prefix{v}suffix") See https://pulumi.io/help/outputs for more details.
    

    The reason this is happening is because the Output value is being used before it has returned and is available from the API.

    A concrete example of this can be seen in the following code:

    The following code is an example of what NOT to do. Please do not copy this code into your Pulumi program
    // NOTE: This example is not correct
    let contentBucket = new aws.s3.Bucket("content-bucket", {
      acl: "private",
      website: {
        indexDocument: "index.html",
        errorDocument: "index.html",
      },
      forceDestroy: true,
    });
    
    let bucketPolicy = new aws.s3.BucketPolicy("cloudfront-bucket-policy", {
      bucket: contentBucket.bucket,
      policy: JSON.stringify({
            Version: "2012-10-17",
            Statement: [
              {
                Sid: "CloudfrontAllow",
                Effect: "Allow",
                Principal: {
                  AWS: iamArn,
                },
                Action: "s3:GetObject",
                Resource: bucket.arn.apply(arn => arn),
              },
            ],
          })
    });
    
    // NOTE: This example is not correct
    const contentBucket = new aws.s3.Bucket("content-bucket", {
      acl: "private",
      website: {
        indexDocument: "index.html",
        errorDocument: "index.html",
      },
      forceDestroy: true,
    });
    
    const bucketPolicy = new aws.s3.BucketPolicy("cloudfront-bucket-policy", {
      bucket: contentBucket.bucket,
      policy: JSON.stringify({
            Version: "2012-10-17",
            Statement: [
              {
                Sid: "CloudfrontAllow",
                Effect: "Allow",
                Principal: {
                  AWS: iamArn,
                },
                Action: "s3:GetObject",
                Resource: bucket.arn.apply(arn => arn),
              },
            ],
          })
    });
    
    # NOTE: This example is not correct
    bucket = aws.s3.Bucket(
        "cloudfront",
        acl="private",
        website=aws.s3.BucketWebsiteArgs(
            index_document="index.html", error_document="404.html"
        ),
    )
    
    bucket_policy = aws.s3.BucketPolicy(
        "cloudfrontAccess",
        bucket=bucket.bucket,
        policy=json.dumps(
                {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "CloudfrontAllow",
                            "Effect": "Allow",
                            "Principal": {
                                "AWS": "*",
                            },
                            "Action": "s3:GetObject",
                            "Resource": bucket.arn.apply(lambda arn: arn),
                        }
                    ],
                }
            )
        ),
        opts=pulumi.ResourceOptions(parent=bucket)
    )
    
    
    // NOTE: This example is not correct
    bucket, err := s3.NewBucket(ctx, "content-bucket", &s3.BucketArgs{
    	Acl: pulumi.String("private"),
    	Website: &s3.BucketWebsiteArgs{
    		IndexDocument: pulumi.String("index.html"),
    		ErrorDocument: pulumi.String("404.html"),
    	},
    })
    if err != nil {
    	return err
    }
    
    _, err = s3.NewBucketPolicy(ctx, "cloudfront-bucket-policy", &s3.BucketPolicyArgs{
    	Bucket: bucket.ID(),
    	Policy: json.Marshal(map[string]interface{}{
    				"Version": "2012-10-17",
    				"Statement": []map[string]interface{}{
    					{
    						"Sid":    "CloudfrontAllow",
    						"Effect": "Allow",
    						"Principal": map[string]interface{}{
    							"AWS": iamArn,
    						},
    						"Action":   "s3:GetObject",
    						"Resource": bucket.Arn.ApplyT(arn []interface{} (pulumi.StringOutput)),
    					},
    				},
    			})
    		}),
    if err != nil {
    	return err
    }
    
    // NOTE: this example is not correct
    var bucket = new Bucket("content-bucket", new BucketArgs
    {
        Acl = "private",
        Website = new BucketWebsiteArgs
        {
            IndexDocument = "index.html",
            ErrorDocument = "404.html",
        },
    });
    
     var bucketPolicy = new BucketPolicy("cloudfront-bucket-policy", new BucketPolicyArgs
        {
            Bucket = bucket.Id,
            Policy = JsonSerializer.Serialize(new
            {
                Version = "2012-10-17",
                Statement = new[]
                 {
                    new
                    {
                        Sid = "CloudfrontAllow",
                        Effect = "Allow",
                        Principal = new
                        {
                            AWS = "*"
                        },
                        Action = "s3:GetObject",
                        Resource = bucket.Arn.Apply(arn => $"{arn}/*"),
                    }
                }
            })
        });
    
    // NOTE: this example is not correct
    var bucket = new Bucket("my-bucket", BucketArgs.builder()
        .acl("private")
        .website(BucketWebsiteArgs.builder()
            .indexDocument("index.html")
            .errorDocument("404.html")
            .build())
        .build());
    
    var policy = new JSONObject();
                policy.put("Version", "2012-10-17");
                policy.put("Statement", new JSONArray(new JSONObject[] {
                    new JSONObject()
                        .put("Effect", "Allow")
                        .put("Principal", "*")
                        .put("Action", "s3:GetObject")
                        .put("Resource", bucket.apply.arn(arn -> arn) + "/*")
                }));
    
                var bucketPolicy = new BucketPolicy("cloudfront-bucket-policy", BucketPolicyArgs.builder()
                        .bucket(bucket.id())
                        .policy(policy)
                        .build());
    

    Notice how the apply call is being used inside the JSON string. In this scenario, the apply call is happening during the build of the JSON string, which is too early in the Pulumi lifecycle. The value of the bucket ARN has not yet been returned from the cloud provider API, so the JSON string cannot be built yet.

    The correct way of handling this scenario is to wait for the bucket ARN to return, then build the JSON string. You can see an example of this in the (#outputs-and-json) section.

    Notice in that example how the JSON string is being built inside the apply call to Pulumi. In logical order, this happens as:

    • Wait for the bucket ARN to be returned from the cloud provider
    • Then, build the JSON string with the “plain” value.

    Resource Names

    When creating multiple resources, it can be tempting to use the output of one resource as the name to another:

    const bucket = new aws.s3.Bucket("my-bucket", {});
    
    const bucketPolicy = new aws.s3.BucketPolicy(bucket.name, {
      // rest of bucket policy arguments go here
    });
    
    // NOTE: This example is not correct
    const bucket = new aws.s3.Bucket("my-bucket", {});
    
    const bucketPolicy = new aws.s3.BucketPolicy(bucket.name, {
      // rest of bucket policy arguments go here
    });
    
    # NOTE: This example is not correct
    bucket = aws.s3.Bucket("my-bucket",)
    
    bucket_policy = aws.s3.BucketPolicy(
        bucket.name,
       # rest of bucket policy arguments go here
    )
    
    
    // NOTE: This example is not correct
    bucket, err := s3.NewBucket(ctx, "my-bucket", &s3.BucketArgs{})
    if err != nil {
    	return err
    }
    
    _, err = s3.NewBucketPolicy(ctx, bucket.Name, &s3.BucketPolicyArgs{
    	Bucket: bucket.ID(),
    	// rest of bucket policy arguments go here
    })
    if err != nil {
    	return err
    }
    
    var bucket = new Bucket("my-bucket", new BucketArgs{});
    
    var bucketPolicy = new BucketPolicy(bucket.Name, new BucketPolicyArgs
    {
        Bucket = bucket.Id,
        // rest of the bucket policy arguments go here
    })
    
    var bucket = new Bucket("my-bucket")
    var bucketPolicy = new BucketPolicy(bucket.bucket(),
        BucketPolicyArgs.builder()
        // the rest of the bucket policy arguments go here);
    

    However, this pattern isn’t possible because resource names need to be available and known at runtime in order to construct the URN of the resource.

    If you really wish to pass an output from one resource as the resource name of another resource, you will need to use a apply apply Apply Apply .

    This will lead to previews being inaccurate. Resources created inside an apply will only appear in the Pulumi output after an up operation is run, and is therefore strongly discouraged.

    JavaScript Promises

    If you’re familiar with JavaScript, you might already be familiar with the mechanism of Promises.

    Pulumi Output values are similar in some ways to JavaScript Promises, in that they are values that are not necessarily known until a future event. With that in mind, it can be tempting to try and use Promise.resolve() and to think of Outputs like Promises.

    However, Pulumi Outputs do not behave in the same way as JavaScript Promises. As such, it’s not possible to block execution of a Pulumi program until a promise is resolved and you should not use JavaScript Promise mechanisms to handle Pulumi Outputs.

      Pulumi AI - What cloud infrastructure would you like to build? Generate Program