Skip to main content
  1. Docs
  2. Infrastructure as Code
  3. Concepts
  4. Inputs & Outputs
  5. Accessing single outputs with Apply

Accessing single outputs with Apply

    The apply method is used to access the plain value of a single output and perform operations on it. Because outputs are asynchronous values that only become known after a resource has finished provisioning, you cannot directly access or manipulate their values using standard language operations (like printing or string concatenation). The apply method solves this by waiting for the output value to become available and then executing a function with that plain value.

    The apply method is typically used for:

    For more information about what outputs are and why they are necessary in Pulumi programs, see Inputs and Outputs.

    The apply method is designed for accessing single output values. If you need to access multiple output values across multiple resources, use Pulumi’s all method instead.

    Creating resources inside an apply should be avoided whenever possible. Resources created inside apply will not appear in pulumi preview unless the output’s value is already known. This means the preview output may not match the actual changes when pulumi up is run, making it difficult to understand what changes will be made to your infrastructure.

    If you need to create a resource that depends on an output value, pass the output directly as an input to the resource instead of using apply. Pulumi will automatically handle the dependency tracking and ensure resources are created in the correct order.

    You cannot create stack outputs (using export in TypeScript/JavaScript, pulumi.export() in Python, ctx.Export() in Go, etc.) inside an apply. Stack outputs must be created at the top level of your Pulumi program. If you need to export a value that depends on an output, you can export the output directly—Pulumi will automatically handle resolving the value when the stack output is accessed.
    The apply method does not apply to Pulumi YAML. YAML is a declarative language with no facility for running a function against a resolved value, so it has no apply equivalent. To use an output value in a YAML program, reference it directly with interpolation syntax (for example, ${myResource.myProperty}) and Pulumi resolves the value for you. For this reason, the examples on this page do not include YAML.

    Printing output values

    Suppose you want to print the ID of a resource you’ve created. These kinds of values are outputs - values that cannot be known until after a resource is provisioned. You might try logging the value like you would any other string:

    import * as pulumi from "@pulumi/pulumi";
    import * as awsx from "@pulumi/awsx";
    
    const vpc = new awsx.ec2.Vpc("vpc");
    
    console.log(vpc.vpcId);
    
    import pulumi
    import pulumi_awsx as awsx
    
    vpc = awsx.ec2.Vpc("vpc")
    
    print(vpc.vpc_id)
    
    package main
    
    import (
        "fmt"
    	"github.com/pulumi/pulumi-awsx/sdk/v3/go/awsx/ec2"
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    
    		vpc, err := ec2.NewVpc(ctx, "vpc", nil)
    		if err != nil {
    			return err
    		}
    
            fmt.Println(vpc.VpcId)
    
    		return nil
    	})
    }
    
    using Pulumi;
    using System.Collections.Generic;
    using Pulumi.Awsx.Ec2;
    
    return await Deployment.RunAsync(() =>
    {
        var vpc = new Vpc("vpc");
    
        Console.WriteLine(vpc.VpcId);
    
    });
    
    package myproject;
    
    import com.pulumi.Pulumi;
    import com.pulumi.awsx.ec2.Vpc;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(ctx -> {
    
                var vpc = new Vpc("vpc");
    
                System.out.println(vpc.vpcId());
    
            });
        }
    }
    

    However, if you run the program as shown with pulumi up, you will receive something like the following CLI output:

    # Example CLI output (truncated)
    Diagnostics:
      pulumi:pulumi:Stack (aws-js-dev):
        OutputImpl {
          __pulumiOutput: true,
          resources: [Function (anonymous)],
          allResources: [Function (anonymous)],
          isKnown: Promise { <pending> },
          isSecret: Promise { <pending> },
          promise: [Function (anonymous)],
          toString: [Function (anonymous)],
          toJSON: [Function (anonymous)]
        }
    
    # Example CLI output (truncated)
    Diagnostics:
      pulumi:pulumi:Stack (aws-iac-dev):
        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://www.pulumi.com/docs/concepts/inputs-outputs for more details.
        This function may throw in a future version of Pulumi.
    
    # Example CLI output (truncated)
    Diagnostics:
      pulumi:pulumi:Stack (aws-go-dev):
        {0xc000137180}
    
    # Example CLI output (truncated)
    Diagnostics:
      pulumi:pulumi:Stack (aws-csharp-dev):
        Calling [ToString] on an [Output<T>] is not supported.
        To get the value of an Output<T> as an Output<string> consider:
        1. o.Apply(v => $"prefix{v}suffix")
        2. Output.Format($"prefix{hostname}suffix");
        See https://www.pulumi.com/docs/concepts/inputs-outputs for more details.
        This function may throw in a future version of Pulumi.
    
    # Example CLI output (truncated)
    Updating (pulumi/dev)
        Type                                          Name           Status              Info
     +   pulumi:pulumi:Stack                           aws-java-dev     created (1s)        391 messages
     +   └─ awsx:ec2:Vpc                               vpc            created (1s)
    ...
    ...
    
    # Nothing is printed
    
    Resources:
        + 34 created
    
    Duration: 2m17s
    

    This is where apply apply Apply Apply comes into play: When a Pulumi program is executed with pulumi up, the apply apply Apply Apply function will wait for the resource to be created and for its properties to be resolved before printing the desired value of the property.

    To print out the value of the VPC ID, use the apply function:

    import * as pulumi from "@pulumi/pulumi";
    import * as awsx from "@pulumi/awsx";
    
    const vpc = new awsx.ec2.Vpc("vpc");
    
    vpc.vpcId.apply(id => console.log(`VPC ID: ${id}`));
    
    import pulumi
    import pulumi_awsx as awsx
    
    vpc = awsx.ec2.Vpc("vpc")
    
    vpc.vpc_id.apply(lambda id: print('VPC ID:', id))
    
    package main
    
    import (
        "fmt"
    	"github.com/pulumi/pulumi-awsx/sdk/v3/go/awsx/ec2"
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    
    		vpc, err := ec2.NewVpc(ctx, "vpc", nil)
    		if err != nil {
    			return err
    		}
    
            vpc.VpcId().ApplyT(func(id string) error {
                fmt.Printf("VPC ID: %s", id)
            	return nil
            })
    
    		return nil
    	})
    }
    
    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 accommodates 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. For details on how errors returned from the callback surface during an update, see Handling errors in apply.
    using Pulumi;
    using System.Collections.Generic;
    using Pulumi.Awsx.Ec2;
    
    return await Deployment.RunAsync(() =>
    {
        var vpc = new Vpc("vpc");
    
        vpc.VpcId.Apply(id => { Console.WriteLine($"VPC ID: {id}"); return id; });
    
    });
    
    package myproject;
    
    import com.pulumi.Pulumi;
    import com.pulumi.awsx.ec2.Vpc;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(ctx -> {
    
                var vpc = new Vpc("vpc");
    
                vpc.vpcId().applyValue(i -> {
                    System.out.println("VPC ID: " + i);
                    return null;
                });
    
            });
        }
    }
    

    The above example will wait for the value to be returned from the API and print it to the console as shown below:

    Updating (pulumi/dev)
    
         Type                 Name         Status     Info
         pulumi:pulumi:Stack  aws-iac-dev             1 message
    
    Diagnostics:
      pulumi:pulumi:Stack (aws-iac-dev):
        VPC ID: vpc-0f8a025738f2fbf2f
    

    Accessing nested output values

    Sometimes a resource has an output property that is an array or a more complex object multiple levels of nested values. For example, if you created an AWS Certificate Manager certificate resource as shown below:

    import * as aws from "@pulumi/aws";
    
    const zone = new aws.route53.Zone("zone", {
        name: "example.com",
    });
    
    const cert = new aws.acm.Certificate("cert", {
        domainName: "example.com",
        validationMethod: "DNS",
    });
    
    import pulumi_aws as aws
    
    zone = aws.route53.Zone("zone",
        name="example.com",
    )
    
    certificate = aws.acm.Certificate("cert",
        domain_name="example.com",
        validation_method="DNS",
    )
    
    package main
    
    import (
    	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/acm"
    	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/route53"
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    		zone, err := route53.NewZone(ctx, "zone", &route53.ZoneArgs{
    			Name: pulumi.String("example.com"),
    		})
    		if err != nil {
    			return err
    		}
    
    		cert, err := acm.NewCertificate(ctx, "cert", &acm.CertificateArgs{
    			DomainName:       pulumi.String("example.com"),
    			ValidationMethod: pulumi.String("DNS"),
    		})
    		if err != nil {
    			return err
    		}
    
    using Pulumi;
    using Pulumi.Aws.Acm;
    using Pulumi.Aws.Route53;
    
    return await Deployment.RunAsync(() =>
    {
        var zone = new Zone("zone", new ZoneArgs
        {
            Name = "example.com",
        });
    
        var cert = new Certificate("cert", new CertificateArgs
        {
            DomainName = "example.com",
            ValidationMethod = "DNS",
        });
    
    package myproject;
    
    import com.pulumi.Pulumi;
    import com.pulumi.aws.acm.Certificate;
    import com.pulumi.aws.acm.CertificateArgs;
    import com.pulumi.aws.route53.Record;
    import com.pulumi.aws.route53.RecordArgs;
    import com.pulumi.aws.route53.Zone;
    import com.pulumi.aws.route53.ZoneArgs;
    import com.pulumi.core.Either;
    import java.util.List;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(ctx -> {
                var zone = new Zone("zone",
                    ZoneArgs.builder()
                        .name("example.com")
                        .build());
    
                var cert = new Certificate("cert",
                    CertificateArgs.builder()
                        .domainName("example.com")
                        .validationMethod("DNS")
    

    This resource will have outputs that resemble the following:

    # Example truncated output of the ACM certificate resource
    cert: {
        arn                      : "arn:aws:acm:eu-central-1..."
        certificate_authority_arn: ""
        certificate_body         : <null>
        certificate_chain        : <null>
        domain_name              : "example.com"
        domain_validation_options: [
            [0]: {
                domain_name          : "example.com"
                resource_record_name : "_0a822dde6347292b.example.com."
                resource_record_type : "CNAME"
                resource_record_value: "_527b1cdf2159204b.mhbtsbpdnt.acm-validations.aws."
            }
        ]
        ...
        ...
    }
    

    Suppose you want to validate your certificate by creating an Amazon Route 53 record. To do so, you will need to retrieve the value of the resource record from the ACM certificate. This value is nested in the domain validation options property of the certificate resource, which is an array. Because that value is an output, you would normally need to use apply apply Apply Apply to retrieve it:

    // Using apply to access nested output values
    const certValidationApply = new aws.route53.Record("certValidationApply", {
        name: cert.domainValidationOptions.apply(
            domainValidationOptions => domainValidationOptions[0].resourceRecordName),
        type: cert.domainValidationOptions.apply(
            domainValidationOptions => domainValidationOptions[0].resourceRecordType),
        zoneId: zone.zoneId,
        ttl: 60,
        records: [
            cert.domainValidationOptions.apply(
                domainValidationOptions => domainValidationOptions[0].resourceRecordValue),
        ],
    });
    
    # Using apply to access nested output values
    cert_validation_apply = aws.route53.Record("certValidationApply",
        name=certificate.domain_validation_options.apply(
            lambda domain_validation_options: domain_validation_options[0].resource_record_name
        ),
        type=certificate.domain_validation_options.apply(
            lambda domain_validation_options: domain_validation_options[0].resource_record_type
        ),
        zone_id=zone.zone_id,
        ttl=60,
        records=[
            certificate.domain_validation_options.apply(
                lambda domain_validation_options: domain_validation_options[0].resource_record_value
            )
        ],
    )
    
    		// Using apply to access nested output values
    		_, err = route53.NewRecord(ctx, "certValidationApply", &route53.RecordArgs{
    			Name: cert.DomainValidationOptions.ApplyT(func(opts []acm.CertificateDomainValidationOption) string {
    				return *opts[0].ResourceRecordName
    			}).(pulumi.StringOutput),
    			Type: cert.DomainValidationOptions.ApplyT(func(opts []acm.CertificateDomainValidationOption) string {
    				return *opts[0].ResourceRecordType
    			}).(pulumi.StringOutput),
    			ZoneId: zone.ZoneId,
    			Ttl:    pulumi.Int(60),
    			Records: pulumi.StringArray{
    				cert.DomainValidationOptions.ApplyT(func(opts []acm.CertificateDomainValidationOption) string {
    					return *opts[0].ResourceRecordValue
    				}).(pulumi.StringOutput),
    			},
    		})
    		if err != nil {
    			return err
    		}
    
        // Using apply to access nested output values
        var certValidationApply = new Record("certValidationApply", new RecordArgs
        {
            Name = cert.DomainValidationOptions.Apply(opts => opts[0].ResourceRecordName!),
            Type = cert.DomainValidationOptions.Apply(opts => opts[0].ResourceRecordType!),
            ZoneId = zone.ZoneId,
            Ttl = 60,
            Records = { cert.DomainValidationOptions.Apply(opts => opts[0].ResourceRecordValue!) },
        });
    
    
                // Using apply to access nested output values
                var certValidation = new Record("certValidationApply",
                    RecordArgs.builder()
                        .name(cert.domainValidationOptions()
                            .applyValue(opts -> opts.get(0).resourceRecordName().get()))
                        .type(cert.domainValidationOptions()
                            .applyValue(opts -> Either.ofLeft(opts.get(0).resourceRecordType().get())))
                        .zoneId(zone.zoneId())
                        .ttl(60)
                        .records(cert.domainValidationOptions()
                            .applyValue(opts -> opts.get(0).resourceRecordValue().get())
                            .applyValue(String::valueOf)
                            .applyValue(List::of))
    

    Using lifting to simplify nested access

    An easier way to access deeply nested properties is by using lifting. Lifting allows you to access properties and elements directly from a Output Output<T> Output[T] Output Output<T> without needing an apply apply Apply Apply .

    Lifting is handled automatically by Pulumi’s type system, making it largely transparent to use. When you access a property or array element on an output value, Pulumi automatically “lifts” that access into the output context, returning a new output that will resolve to that nested value. 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.

    Lifting works in most scenarios for accessing nested properties and array elements. However, you may occasionally encounter runtime errors with lifting depending on your language. For example, in TypeScript, if an output resolves to undefined, using lifting to reference outputThatResolvesToUndefined.someProperty would cause a runtime error. In such cases, use apply with appropriate null checking instead.

    Returning to the certificate validation example from the previous section, you can use lifting to simplify the code as shown below:

    // Using lifting to access nested output values
    const certValidationLifting = new aws.route53.Record("certValidationLifting", {
        name: cert.domainValidationOptions[0].resourceRecordName,
        type: cert.domainValidationOptions[0].resourceRecordType,
        zoneId: zone.zoneId,
        ttl: 60,
        records: [
            cert.domainValidationOptions[0].resourceRecordValue,
        ],
    });
    
    # Using lifting to access nested output values
    cert_validation_lifting = aws.route53.Record("certValidationLifting",
        name=certificate.domain_validation_options[0].resource_record_name,
        type=certificate.domain_validation_options[0].resource_record_type,
        zone_id=zone.zone_id,
        ttl=60,
        records=[
            certificate.domain_validation_options[0].resource_record_value
        ],
    )
    
    Python implementation note: In Python, output lifting is implemented by overriding the special __getattr__ method on resources. The expression resource.output (which results in a call to resource.__getattr__("output")) becomes resource.apply(lambda r: r.output). This means that using hasattr, which calls __getattr__ under the hood and looks for an AttributeError to determine whether or not a property exists, will not work as expected on resource outputs.
    		// Using lifting to access nested output values
    		_, err = route53.NewRecord(ctx, "certValidationLifting", &route53.RecordArgs{
    			Name: cert.DomainValidationOptions.Index(pulumi.Int(0)).ResourceRecordName().Elem(),
    			Type: cert.DomainValidationOptions.Index(pulumi.Int(0)).ResourceRecordType().Elem(),
    			ZoneId: zone.ZoneId,
    			Ttl:    pulumi.Int(60),
    			Records: pulumi.StringArray{
    				cert.DomainValidationOptions.Index(pulumi.Int(0)).ResourceRecordValue().Elem(),
    			},
    		})
    		if err != nil {
    			return err
    		}
    
        // Using lifting to access nested output values
        var certValidationLifting = new Record("certValidationLifting", new RecordArgs
        {
            Name = cert.DomainValidationOptions.GetAt(0).Apply(opt => opt.ResourceRecordName!),
            Type = cert.DomainValidationOptions.GetAt(0).Apply(opt => opt.ResourceRecordType!),
            ZoneId = zone.ZoneId,
            Ttl = 60,
            Records = { cert.DomainValidationOptions.GetAt(0).Apply(opt => opt.ResourceRecordValue!) },
        });
    
    // Lifting is currently not supported in Java.
    // Use apply as shown in the previous section.
    

    Creating new output values

    Outputs and strings

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

    For the common case of building a string from output values, Pulumi’s output helpers provide a more concise alternative that doesn’t require calling apply directly.

    For example, the following code creates an HTTPS URL from the DNS name (the plain value) of a virtual machine (in this case an EC2 instance):

    import * as pulumi from "@pulumi/pulumi";
    import * as aws from "@pulumi/aws";
    
    const server = new aws.ec2.Instance("web-server", {
        ami: "ami-0319ef1a70c93d5c8",
        instanceType: "t2.micro",
    });
    
    const url = server.publicDns.apply(dnsName => `https://${dnsName}`);
    
    export const InstanceUrl = url;
    
    import pulumi
    import pulumi_aws as aws
    
    server = aws.ec2.Instance(
        "web-server",
        ami="ami-0319ef1a70c93d5c8",
        instance_type="t2.micro",
    )
    
    url = server.public_dns.apply(
        lambda dns_name: "https://" + dns_name
    )
    
    pulumi.export("InstanceUrl", url)
    
    package main
    
    import (
    	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    		server, err := ec2.NewInstance(ctx, "web-server", &ec2.InstanceArgs{
    			Ami:                 pulumi.String("ami-0319ef1a70c93d5c8"),
    			InstanceType:        pulumi.String("t2.micro"),
    		})
    		if err != nil {
    			return err
    		}
    
            url := server.PublicDns.ApplyT(func(dns string) string {
    			return "https://" + dns
    		}).(pulumi.StringOutput)
    
            ctx.Export("InstanceUrl", url)
    		return nil
    	})
    }
    
    using Pulumi;
    using Pulumi.Aws.Ec2;
    using Pulumi.Aws.Ec2.Inputs;
    using System.Collections.Generic;
    
    return await Deployment.RunAsync(() =>
    {
        var server = new Instance("web-server", new InstanceArgs {
            Ami = "ami-0319ef1a70c93d5c8",
            InstanceType = "t2.micro",
        });
    
        var url = server.PublicDns.Apply(dns => $"https://{dns}");
    
        return new Dictionary<string, object?>
        {
            ["InstanceUrl"] = url,
        };
    });
    
    package myproject;
    
    import com.pulumi.Context;
    import com.pulumi.Pulumi;
    import com.pulumi.aws.ec2.Instance;
    import com.pulumi.aws.ec2.InstanceArgs;
    import com.pulumi.aws.ec2.SecurityGroup;
    import com.pulumi.aws.ec2.SecurityGroupArgs;
    import com.pulumi.aws.ec2.inputs.SecurityGroupIngressArgs;
    
    import java.util.List;
    
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(App::stack);
        }
    
        public static void stack(Context ctx) {
            var server = new Instance("web-server",
                InstanceArgs.builder()
                    .ami("ami-0319ef1a70c93d5c8")
                    .instanceType("t2.micro")
                    .build());
    
            var url = server.publicDns().applyValue(dns -> "https://" + dns);
    
            ctx.export("InstanceUrl", url);
        }
    }
    

    The CLI output of this code would look something like the following:

    Updating (pulumi/dev)
    
         Type                 Name         Status
         pulumi:pulumi:Stack  aws-iac-dev
     -   └─ awsx:ec2:Vpc      vpc
    
    Outputs:
        InstanceUrl: "https://ec2-52-59-110-22.eu-central-1.compute.amazonaws.com"
    
    Duration: 5s
    

    The returned value of the call to apply apply Apply Apply is a new pulumi.Output<string>, meaning the url variable is now of an Output. This variable will wait for the new value to be returned from the apply apply Apply Apply function, and any dependencies of the original output (i.e. the public DNS property of the server resource) are also kept in the resulting pulumi.Output<string>.

    Outputs and JSON

    Many cloud resources require JavaScript Object Notation (JSON) documents, such as policies that control access to resources. The Pulumi SDK provides helper methods in most languages to make it easier to work with Pulumi outputs and JSON documents. These helper methods have similar names and function signatures to their plain-value analogues. For a consolidated reference of all JSON helpers, see Using output helpers.

    Converting JSON objects to strings

    If you need to construct a JSON string using output values from Pulumi resources, you can do so using a JSON stringify helper that is defined in the Pulumi SDK. These helpers unwrap Pulumi outputs without requiring the use of apply and produce JSON string outputs suitable for passing to other resources as inputs.

    The following example demonstrates using helper methods for JSON serialization:

    import * as pulumi from "@pulumi/pulumi";
    import * as aws from "@pulumi/aws";
    
    // Get the account ID of the current user as a Pulumi output.
    const accountID = aws.getCallerIdentityOutput().accountId;
    
    // Create an S3 bucket.
    const bucket = new aws.s3.Bucket("my-bucket");
    
    // Create an S3 bucket policy allowing anyone in the account to list the contents of the bucket.
    const policy = new aws.s3.BucketPolicy("my-bucket-policy", {
        bucket: bucket.id,
        policy: pulumi.jsonStringify({
            Version: "2012-10-17",
            Statement: [
                {
                    Effect: "Allow",
                    Principal: {
                        AWS: pulumi.interpolate`arn:aws:iam::${accountID}:root`,
                    },
                    Action: "s3:ListBucket",
                    Resource: bucket.arn,
                },
            ],
        }),
    });
    
    // Export the name of the bucket.
    export const bucketName = bucket.id;
    
    import pulumi
    import pulumi_aws as aws
    
    # Get the account ID of the current user as a Pulumi output.
    account_id = aws.get_caller_identity_output().apply(
        lambda identity: identity.account_id
    )
    
    # Create an S3 bucket.
    bucket = aws.s3.Bucket("my-bucket")
    
    # Create an S3 bucket policy allowing anyone in the account to list the contents of the bucket.
    policy = aws.s3.BucketPolicy(
        "my-bucket-policy",
        bucket=bucket.id,
        policy=pulumi.Output.json_dumps(
            {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": {
                            "AWS": pulumi.Output.format("arn:aws:iam::{0}:root", account_id)
                        },
                        "Action": "s3:ListBucket",
                        "Resource": bucket.arn,
                    }
                ],
            }
        ),
    )
    
    # Export the name of the bucket
    pulumi.export("bucketName", bucket.id)
    
    package main
    
    import (
    	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
    	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    
    		// Get the account ID of the current user as a Pulumi Output.
    		callerIdentity, err := aws.GetCallerIdentity(ctx, nil, nil)
    		if err != nil {
    			return err
    		}
    		accountID := callerIdentity.AccountId
    
    		// Create an AWS resource (S3 Bucket)
    		bucket, err := s3.NewBucket(ctx, "my-bucket", nil)
    		if err != nil {
    			return err
    		}
    
    		// Create an S3 bucket policy allowing anyone in the account to list the contents of the bucket.
    		_, err = s3.NewBucketPolicy(ctx, "my-bucket-policy", &s3.BucketPolicyArgs{
    			Bucket: bucket.ID(),
    			Policy: pulumi.JSONMarshal(map[string]interface{}{
    				"Version": pulumi.ToOutput("2012-10-17"),
    				"Statement": pulumi.ToOutput([]interface{}{
    					pulumi.ToMapOutput(map[string]pulumi.Output{
    						"Effect": pulumi.ToOutput("Allow"),
    						"Principal": pulumi.ToMapOutput(map[string]pulumi.Output{
    							"AWS": pulumi.Sprintf("arn:aws:iam::%s:root", accountID),
    						}),
    						"Action":   pulumi.ToOutput("s3:ListBucket"),
    						"Resource": bucket.Arn,
    					}),
    				}),
    			}),
    		})
    		if err != nil {
    			return err
    		}
    
    		// Export the name of the bucket
    		ctx.Export("bucketName", bucket.ID())
    		return nil
    	})
    }
    
    using System.Collections.Generic;
    using Pulumi;
    using Pulumi.Aws.S3;
    
    return await Deployment.RunAsync(() =>
    {
        // Get the account ID of the current user as a Pulumi output.
        var accountID = Pulumi.Aws.GetCallerIdentity.Invoke().Apply(identity => identity.AccountId);
    
        // Create an S3 bucket.
        var bucket = new Bucket("my-bucket");
    
        // Create an S3 bucket policy allowing anyone in the account to list the contents of the bucket.
        var policy = new BucketPolicy("my-bucket-policy", new BucketPolicyArgs
            {
                Bucket = bucket.Id,
                Policy = Output.JsonSerialize(Output.Create(
                    new
                    {
                        Version = "2012-10-17",
                        Statement = new[]
                        {
                            new
                            {
                                Effect = "Allow",
                                Principal = new
                                {
                                    AWS = Output.Format($"arn:aws:iam::{accountID}:root")
                                },
                                Action = "s3:ListBucket",
                                Resource = bucket.Arn,
                            }
                        }
                    }
                ))
            }
        );
    
        // Export the name of the bucket.
        return new Dictionary<string, object?> { ["bucketName"] = bucket.Id };
    });
    
    package myproject;
    
    import static com.pulumi.codegen.internal.Serialization.*;
    
    import com.pulumi.Pulumi;
    import com.pulumi.core.Either;
    import com.pulumi.core.Output;
    import com.pulumi.aws.AwsFunctions;
    import com.pulumi.aws.s3.Bucket;
    import com.pulumi.aws.s3.BucketPolicy;
    import com.pulumi.aws.s3.BucketPolicyArgs;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(ctx -> {
                // Get the account ID of the current user as a Pulumi output.
                var accountID = AwsFunctions.getCallerIdentity().applyValue(identity -> identity.accountId());
    
                // Create an S3 bucket.
                var bucket = new Bucket("my-bucket");
    
                // Build the policy document, waiting for the account ID and bucket ARN to resolve.
                var policyDocument = Output.tuple(accountID, bucket.arn()).applyValue(t -> serializeJson(
                    jsonObject(
                        jsonProperty("Version", "2012-10-17"),
                        jsonProperty("Statement", jsonArray(jsonObject(
                            jsonProperty("Effect", "Allow"),
                            jsonProperty("Principal", jsonObject(
                                jsonProperty("AWS", "arn:aws:iam::" + t.t1 + ":root")
                            )),
                            jsonProperty("Action", "s3:ListBucket"),
                            jsonProperty("Resource", t.t2)
                        )))
                    )
                ));
    
                // Create an S3 bucket policy allowing anyone in the account to list the contents of the bucket.
                var policy = new BucketPolicy("my-bucket-policy", BucketPolicyArgs.builder()
                    .bucket(bucket.id())
                    .policy(policyDocument.applyValue(Either::ofLeft))
                    .build());
    
                // Export the name of the bucket.
                ctx.export("bucketName", bucket.id());
            });
        }
    }
    

    When constructing a JSON policy document, it is often necessary to build a resource identifier by appending a path suffix to an output value. For example, Amazon S3 bucket policies that apply to objects rather than to the bucket itself require a resource ARN ending in /*. Because the bucket ARN is a Pulumi output, you must combine the JSON stringify helper with your language’s string interpolation facility to produce the correct value.

    The following example demonstrates this pattern, using the bucket’s ARN as a base and appending /* to target all objects in the bucket:

    import * as pulumi from "@pulumi/pulumi";
    import * as aws from "@pulumi/aws";
    
    // Create an S3 bucket.
    const bucket = new aws.s3.Bucket("my-bucket");
    
    // Create an S3 bucket policy that grants read access to all objects in the bucket.
    // The Resource field uses pulumi.interpolate to append "/*" to the bucket ARN,
    // targeting individual objects rather than the bucket itself.
    const policy = new aws.s3.BucketPolicy("my-bucket-policy", {
        bucket: bucket.id,
        policy: pulumi.jsonStringify({
            Version: "2012-10-17",
            Statement: [
                {
                    Effect: "Allow",
                    Principal: "*",
                    Action: "s3:GetObject",
                    Resource: pulumi.interpolate`${bucket.arn}/*`,
                },
            ],
        }),
    });
    
    // Export the name of the bucket.
    export const bucketName = bucket.id;
    
    import pulumi
    import pulumi_aws as aws
    
    # Create an S3 bucket.
    bucket = aws.s3.Bucket("my-bucket")
    
    # Create an S3 bucket policy that grants read access to all objects in the bucket.
    # The Resource field uses pulumi.Output.concat to append "/*" to the bucket ARN,
    # targeting individual objects rather than the bucket itself.
    policy = aws.s3.BucketPolicy(
        "my-bucket-policy",
        bucket=bucket.id,
        policy=pulumi.Output.json_dumps(
            {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": "*",
                        "Action": "s3:GetObject",
                        "Resource": pulumi.Output.concat(bucket.arn, "/*"),
                    }
                ],
            }
        ),
    )
    
    # Export the name of the bucket.
    pulumi.export("bucketName", bucket.id)
    
    package main
    
    import (
    	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    
    		// Create an S3 bucket.
    		bucket, err := s3.NewBucket(ctx, "my-bucket", nil)
    		if err != nil {
    			return err
    		}
    
    		// Create an S3 bucket policy that grants read access to all objects in the bucket.
    		// The Resource field uses pulumi.Sprintf to append "/*" to the bucket ARN,
    		// targeting individual objects rather than the bucket itself.
    		_, err = s3.NewBucketPolicy(ctx, "my-bucket-policy", &s3.BucketPolicyArgs{
    			Bucket: bucket.ID(),
    			Policy: pulumi.JSONMarshal(map[string]interface{}{
    				"Version": pulumi.ToOutput("2012-10-17"),
    				"Statement": pulumi.ToOutput([]interface{}{
    					pulumi.ToMapOutput(map[string]pulumi.Output{
    						"Effect":    pulumi.ToOutput("Allow"),
    						"Principal": pulumi.ToOutput("*"),
    						"Action":    pulumi.ToOutput("s3:GetObject"),
    						"Resource":  pulumi.Sprintf("%s/*", bucket.Arn),
    					}),
    				}),
    			}),
    		})
    		if err != nil {
    			return err
    		}
    
    		// Export the name of the bucket.
    		ctx.Export("bucketName", bucket.ID())
    		return nil
    	})
    }
    
    using System.Collections.Generic;
    using Pulumi;
    using Pulumi.Aws.S3;
    
    return await Deployment.RunAsync(() =>
    {
        // Create an S3 bucket.
        var bucket = new Bucket("my-bucket");
    
        // Create an S3 bucket policy that grants read access to all objects in the bucket.
        // The Resource field uses Output.Format to append "/*" to the bucket ARN,
        // targeting individual objects rather than the bucket itself.
        var policy = new BucketPolicy("my-bucket-policy", new BucketPolicyArgs
            {
                Bucket = bucket.Id,
                Policy = Output.JsonSerialize(Output.Create(
                    new
                    {
                        Version = "2012-10-17",
                        Statement = new[]
                        {
                            new
                            {
                                Effect = "Allow",
                                Principal = "*",
                                Action = "s3:GetObject",
                                Resource = Output.Format($"{bucket.Arn}/*"),
                            }
                        }
                    }
                ))
            }
        );
    
        // Export the name of the bucket.
        return new Dictionary<string, object?> { ["bucketName"] = bucket.Id };
    });
    
    package myproject;
    
    import static com.pulumi.codegen.internal.Serialization.*;
    
    import com.pulumi.Pulumi;
    import com.pulumi.core.Either;
    import com.pulumi.aws.s3.Bucket;
    import com.pulumi.aws.s3.BucketPolicy;
    import com.pulumi.aws.s3.BucketPolicyArgs;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(ctx -> {
                // Create an S3 bucket.
                var bucket = new Bucket("my-bucket");
    
                // Build the policy document, appending "/*" to the bucket ARN so the
                // statement targets individual objects rather than the bucket itself.
                var policyDocument = bucket.arn().applyValue(arn -> serializeJson(
                    jsonObject(
                        jsonProperty("Version", "2012-10-17"),
                        jsonProperty("Statement", jsonArray(jsonObject(
                            jsonProperty("Effect", "Allow"),
                            jsonProperty("Principal", "*"),
                            jsonProperty("Action", "s3:GetObject"),
                            jsonProperty("Resource", arn + "/*")
                        )))
                    )
                ));
    
                // Create an S3 bucket policy that grants read access to all objects in the bucket.
                var policy = new BucketPolicy("my-bucket-policy", BucketPolicyArgs.builder()
                    .bucket(bucket.id())
                    .policy(policyDocument.applyValue(Either::ofLeft))
                    .build());
    
                // Export the name of the bucket.
                ctx.export("bucketName", bucket.id());
            });
        }
    }
    

    Converting JSON strings to outputs

    If you have an output in the form of a JSON string and you need to interact with it like you would a regular JSON object, you can use Pulumi’s parsing helper function.

    The following example shows how to use a helper method to parse an IAM policy defined as a pulumi.Output<string> into a native object and then manipulate that object to remove all of the policy statements:

    import * as pulumi from "@pulumi/pulumi";
    
    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"
            }
        ]
    }`);
    
    // Parse the string output.
    const policyWithNoStatements: pulumi.Output<object> = pulumi.jsonParse(jsonIAMPolicy).apply(policy => {
        // Empty the policy's Statements list.
        policy.Statement = [];
        return policy;
    });
    
    // Export the modified policy.
    export const policy = policyWithNoStatements;
    
    import pulumi
    
    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):
        # Empty the policy's Statements list.
        policy.update({"Statement": []})
        return policy
    
    
    # Parse the string output.
    policy_with_no_statements = pulumi.Output.json_loads(json_iam_policy).apply(
        lambda policy: update_policy
    )
    
    # Export the modified policy.
    pulumi.export("policy", policy_with_no_statements)
    
    package main
    
    import (
    	"encoding/json"
    	
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    
    		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"
    		        }
    		    ]
    		}`)
    
    		// Parse the string output.
    		policyWithNoStatements := jsonIAMPolicy.ApplyT(
    			func(jsonStr string) (map[string]interface{}, error) {
    				var policy map[string]interface{}
    				if err := json.Unmarshal([]byte(jsonStr), &policy); err != nil {
    					return nil, err
    				}
    
    				// Empty the policy's Statements list.
    				policy["Statement"] = []interface{}{}
    				return policy, nil
    			},
    		)
    
    		// Export the modified policy.
    		ctx.Export("policy", policyWithNoStatements)
    		return nil
    	})
    }
    
    using System.Collections.Generic;
    using Pulumi;
    
    return await Deployment.RunAsync(() =>
    {
        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""
                    }
                ]
            }
        "
        );
    
        // Parse the Output<string> into a C# Dictionary.
        var policyWithNoStatements = Output
            .JsonDeserialize<Dictionary<string, object?>>(jsonIAMPolicy)
            .Apply(policy =>
            {
                // Empty the policy's Statements list.
                policy["Statement"] = new List<object?>();
                return policy;
            });
    
        // Export the modified policy.
        return new Dictionary<string, object?> { ["policy"] = policyWithNoStatements, };
    });
    
    package myproject;
    
    import com.pulumi.Pulumi;
    import com.pulumi.core.Output;
    import com.google.gson.Gson;
    import com.google.gson.reflect.TypeToken;
    
    import java.util.ArrayList;
    import java.util.Map;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(ctx -> {
                var jsonIAMPolicy = Output.of("{\n" +
                    "    \"Version\": \"2012-10-17\",\n" +
                    "    \"Statement\": [\n" +
                    "        {\n" +
                    "            \"Sid\": \"VisualEditor0\",\n" +
                    "            \"Effect\": \"Allow\",\n" +
                    "            \"Action\": [\n" +
                    "                \"s3:ListAllMyBuckets\",\n" +
                    "                \"s3:GetBucketLocation\"\n" +
                    "            ],\n" +
                    "            \"Resource\": \"*\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"Sid\": \"VisualEditor1\",\n" +
                    "            \"Effect\": \"Allow\",\n" +
                    "            \"Action\": \"s3:*\",\n" +
                    "            \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
                    "        }\n" +
                    "    ]\n" +
                    "}");
    
                // Parse the string output into a map and empty the policy's Statement list.
                var gson = new Gson();
                var policyWithNoStatements = jsonIAMPolicy.applyValue(json -> {
                    Map<String, Object> policy = gson.fromJson(json, new TypeToken<Map<String, Object>>() {}.getType());
                    policy.put("Statement", new ArrayList<>());
                    return policy;
                });
    
                // Export the modified policy.
                ctx.export("policy", policyWithNoStatements);
            });
        }
    }
    

    For more details view the Go documentation.

    For more details view the .NET documentation.

    Handling errors in apply

    The function you pass to apply apply Apply Apply can fail—for example, when a resolved value doesn’t meet a condition your program requires. In every Pulumi language, an unhandled error raised inside an apply callback is reported as a deployment failure: pulumi up aborts the update and prints the error in its diagnostics. The error is not swallowed (with one Go-specific exception described below), and the language process is not left in an unrecoverable state—you do not need to wrap apply in your language’s try/catch construct to surface it. In fact, raising an error from inside the callback is the idiomatic way to fail an update when a value doesn’t satisfy a requirement.

    How you signal an error from the callback depends on the language.

    Throw an exception from the callback (or return a rejected promise). The thrown error rejects the resulting output and fails the update.

    const validated = name.apply(n => {
        if (!n.includes("a")) {
            throw new Error(`name "${n}" must contain the letter 'a'`);
        }
        return n;
    });
    

    The update fails whether or not validated is later used in the program.

    Raise an exception from the callback. The exception rejects the resulting output and fails the update.

    def validate(n: str) -> str:
        if "a" not in n:
            raise Exception(f'name "{n}" must contain the letter \'a\'')
        return n
    
    validated = name.apply(validate)
    

    The update fails whether or not validated is later used in the program.

    Return a non-nil error as the second return value. The ApplyT callback accepts either a func(T) U or a func(T) (U, error) signature; the second form is what lets you report an error. Returning an error does not panic—it rejects the resulting output.

    validated := name.ApplyT(func(n string) (string, error) {
        if !strings.Contains(n, "a") {
            return "", fmt.Errorf("name %q must contain the letter 'a'", n)
        }
        return n, nil
    })
    
    Unlike the other Pulumi languages, Go surfaces a rejected output’s error only when the output is consumed—that is, exported as a stack output or passed as an input to another resource. If you call ApplyT purely for a side effect (such as printing) and discard the returned output, an error you return from the callback is silently dropped and the update succeeds. To make sure validation errors fail the update, either consume the resulting output or handle the error inside the callback.

    For cancellation-aware error handling, use ApplyTWithContext, whose callback receives a context.Context that can be used to reject the output when the context is canceled.

    Throw an exception from the callback. The thrown error rejects the resulting output and fails the update.

    var validated = name.Apply(n =>
    {
        if (!n.Contains("a"))
        {
            throw new Exception($"name \"{n}\" must contain the letter 'a'");
        }
        return n;
    });
    

    The update fails whether or not validated is later used in the program.

    Throw an exception from the callback. The thrown error rejects the resulting output and fails the update.

    var validated = name.applyValue(n -> {
        if (!n.contains("a")) {
            throw new RuntimeException("name \"" + n + "\" must contain the letter 'a'");
        }
        return n;
    });
    

    The update fails whether or not validated is later used in the program.

    To handle a potential failure gracefully and continue rather than failing the update, do the handling inside the callback—for example, catch the error and return a fallback value instead of raising.

    Converting inputs to outputs

    Resource arguments in Pulumi accept Input<T> values, which means they will take either a plain value or an Output<T>. In most programs this flexibility is all you need. There are situations, however, where you have a value typed as Input<T> and need to ensure it is a definite Output<T>—most commonly to call apply on it.

    This situation arises most often in the following cases:

    • Writing a component resource. Component constructors typically accept Input<T> parameters to give callers the flexibility to pass either a plain value or an existing output. Inside the component body, you often need to call apply or chain other output operations on those parameters, which requires Output<T>.
    • Writing utility functions that accept Input<T>. A function that accepts Input<T> for caller flexibility must convert to Output<T> internally before it can call apply to perform any transformation.
    • Combining values with all. While the all function accepts a mix of plain values and outputs in most SDKs, explicitly converting to outputs first can make your program’s data flow clearer and more predictable.

    Pulumi’s SDKs provide a dedicated function to wrap any Input<T> as a guaranteed Output<T>:

    pulumi.output() accepts any Input<T>—a plain value or an existing Output<T>—and returns Output<T>. The example below defines a helper that requires Output<string> internally, then calls it with both a plain string and with an Output<string> from a resource to show that both work.

    import * as pulumi from "@pulumi/pulumi";
    import * as aws from "@pulumi/aws";
    
    function buildUrl(host: pulumi.Input<string>): pulumi.Output<string> {
        return pulumi.output(host).apply(h => `https://${h}`);
    }
    
    const fromPlain = buildUrl("example.com");
    
    const bucket = new aws.s3.BucketV2("my-bucket");
    const fromOutput = buildUrl(bucket.websiteEndpoint);
    

    pulumi.Output.from_input() accepts any Input[T]—a plain value or an existing Output[T]—and returns Output[T].

    import pulumi
    import pulumi_aws as aws
    
    # A helper function that accepts Input[str] but needs to call apply.
    def build_url(host: pulumi.Input[str]) -> pulumi.Output[str]:
        return pulumi.Output.from_input(host).apply(lambda h: f"https://{h}")
    
    # Works with a plain string.
    from_plain = build_url("example.com")
    
    # Works equally well with an Output[str] from a resource.
    bucket = aws.s3.BucketV2("my-bucket")
    from_output = build_url(bucket.website_endpoint)
    

    In Go, each typed input interface exposes a ToXxxOutput() method that returns the corresponding concrete output type. For example, pulumi.StringInput provides ToStringOutput(). The snippet below defines a helper that requires pulumi.StringOutput internally, then calls it with both a plain value and a StringOutput from a resource.

    import (
        awss3 "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
        "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func buildURL(input pulumi.StringInput) pulumi.StringOutput {
        return input.ToStringOutput().ApplyT(func(host string) string {
            return "https://" + host
        }).(pulumi.StringOutput)
    }
    
    // Inside a Pulumi program:
    fromPlain := buildURL(pulumi.String("example.com"))
    
    bucket, _ := awss3.NewBucketV2(ctx, "my-bucket", nil, nil)
    fromOutput := buildURL(bucket.Bucket)
    

    Each typed input interface in the Go SDK—pulumi.StringInput, pulumi.IntInput, pulumi.BoolInput, and so on—follows this same ToXxxOutput() pattern.

    In C#, Input<T> exposes Apply through Pulumi SDK extension methods, so you can often call Apply without an explicit conversion step:

    // Input<T> supports Apply through extension methods in the Pulumi C# SDK.
    Input<string> host = "example.com"; // could be a plain string or Output<string>
    Output<string> url = host.Apply(h => $"https://{h}");
    

    When you need to construct a standalone Output<T> from a plain value, use Output.Create:

    Output<string> output = Output.Create("example.com");
    

    Output.of() wraps a plain value as an Output<T>. When your value is already an Output<T>, you can use it directly without any conversion.

    import com.pulumi.core.Output;
    
    Output<String> output = Output.of("example.com");
    Output<String> url = output.applyValue(host -> "https://" + host);
    

    When the value you pass is already an Output<T>, the conversion function returns it unchanged. When you pass a plain value, Pulumi wraps it in a new output that resolves immediately with that value. In either case, the result is a definite Output<T> on which you can call apply or any other output method.