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.

    Accessing single 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:

    "use strict";
    const pulumi = require("@pulumi/pulumi");
    const awsx = require("@pulumi/awsx");
    
    const vpc = new awsx.ec2.Vpc("vpc");
    
    console.log(vpc.vpcId);
    
    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-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 example is not applicable in YAML.
    

    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:

    "use strict";
    const pulumi = require("@pulumi/pulumi");
    const awsx = require("@pulumi/awsx");
    
    const vpc = new awsx.ec2.Vpc("vpc");
    
    vpc.vpcId.apply(id => console.log(`VPC ID: ${id}`));
    
    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.
    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;
                });
    
            });
        }
    }
    
    # YAML does not have the Apply method, but you can access values directly.
    name: awsx-vpc-yaml
    runtime: yaml
    description: An example that creates a new VPC using the default settings.
    resources:
      vpc:
        type: awsx:ec2:Vpc
    outputs:
      vpcId: ${vpc.vpcId}
    

    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:

    "use strict";
    const pulumi = require("@pulumi/pulumi");
    const aws = require("@pulumi/aws");
    
    let certCertificate = new aws.acm.Certificate("cert", {
        domainName: "example.com",
        validationMethod: "DNS",
    });
    
    import * as pulumi from "@pulumi/pulumi";
    import * as aws from "@pulumi/aws";
    
    let certCertificate = new aws.acm.Certificate("cert", {
        domainName: "example.com",
        validationMethod: "DNS",
    });
    
    import pulumi
    import pulumi_aws as aws
    
    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/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    		_, err := acm.NewCertificate(ctx, "cert", &acm.CertificateArgs{
    			DomainName:       pulumi.String("example.com"),
    			ValidationMethod: pulumi.String("DNS"),
    		})
    		if err != nil {
    			return err
    		}
    		return nil
    	})
    }
    
    using Pulumi;
    using Pulumi.Aws.Acm;
    using System.Collections.Generic;
    
    return await Deployment.RunAsync(() =>
    {
        var cert = new Certificate("cert", new CertificateArgs
        {
            DomainName = "example",
            ValidationMethod = "DNS",
        });
    });
    
    package myproject;
    
    import com.pulumi.Context;
    import com.pulumi.Pulumi;
    import com.pulumi.core.Output;
    import com.pulumi.aws.acm.Certificate;
    import com.pulumi.aws.acm.CertificateArgs;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(ctx -> {
                var cert = new Certificate("cert",
                    CertificateArgs.builder()
                        .domainName("example")
                        .validationMethod("DNS")
                        .build());
            });
        }
    }
    
    name: aws-acm-certificate-yaml
    runtime: yaml
    
    resources:
      cert:
        type: aws:acm:Certificate
        properties:
          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:

    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 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),
        ],
        ...
    });
    
    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']
            )
        ],
        ...
    )
    
    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 record = new Record("validation", new RecordArgs
    {
        Records = {
            cert.DomainValidationOptions.Apply(opts => opts[0].ResourceRecordValue!)
        },
        ...
    });
    
    var record = new Record("validation",
        RecordArgs.builder()
            .records(
                cert.domainValidationOptions()
                .applyValue(opts -> opts.get(0).resourceRecordValue().get())
                .applyValue(String::valueOf)
                .applyValue(List::of))
            .build());
    
    This example is not applicable in YAML.
    

    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:

    let certValidation = new aws.route53.Record("cert_validation", {
        records: [
            // Lifting: Access nested property directly, without apply
            // Type: certCertificate.domainValidationOptions is Output<Array>
            // Result: certCertificate.domainValidationOptions[0].resourceRecordValue is Output<string>
            certCertificate.domainValidationOptions[0].resourceRecordValue
        ],
    ...
    
    let certValidation = new aws.route53.Record("cert_validation", {
        records: [
            // Lifting: Access nested property directly, without apply
            // Type: certCertificate.domainValidationOptions is Output<Array>
            // Result: certCertificate.domainValidationOptions[0].resourceRecordValue is Output<string>
            certCertificate.domainValidationOptions[0].resourceRecordValue
        ],
    ...
    
    record = aws.route53.Record('validation',
        records=[
            # Lifting: Access nested property directly, without apply
            # Type: certificate.domain_validation_options is Output[List]
            # Result: certificate.domain_validation_options[0].resource_record_value is Output[str]
            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.
    record, err := route53.NewRecord(ctx, "validation", &route53.RecordArgs{
        Records: pulumi.StringArray{
            // Lifting: Access nested property through helper methods
            // Type: cert.DomainValidationOptions is pulumi.ArrayOutput
            // Operations:
            // * `Index` looks up an index in an `ArrayOutput` and returns a new `Output`.
            // * `ResourceRecordValue` is an accessor method that looks up a property of a
            //   custom struct `Output` and returns a new `Output`.
            // * `Elem` dereferences a `PtrOutput` to an `Output`, equivalent to `*`.
            // Result: pulumi.StringOutput
            cert.DomainValidationOptions.Index(pulumi.Int(0)).ResourceRecordValue().Elem(),
        },
        ...
    })
    if err != nil {
        return err
    }
    
    var record = new Record("validation", new RecordArgs
    {
        // Lifting: Partial support in C#
        // Type: cert.DomainValidationOptions is Output<ImmutableArray<T>>
        // Operations:
        // * `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 `Apply` is still needed for the property access.
        Records = cert.DomainValidationOptions.GetAt(0).Apply(opt => opt.ResourceRecordValue!),
    });
    
    // Lifting is currently not supported in Java.
    // Use apply as shown in the previous section.
    
    resources:
      cert:
        type: aws:acm:Certificate
        properties:
          domainName: example
          validationMethod: DNS
      record:
        type: aws:route53:Record
        properties:
          records:
            # YAML handles inputs and outputs transparently.
            # Type: cert.domainValidationOptions is an array output
            # Result: A string output that resolves to the resource record value
            - ${cert.domainValidationOptions[0].resourceRecordValue}
    

    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 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):

    "use strict";
    const pulumi = require("@pulumi/pulumi");
    const aws = require("@pulumi/aws");
    
    const server = new aws.ec2.Instance("web-server", {
        ami: "ami-0319ef1a70c93d5c8",
        instanceType: "t2.micro",
    });
    
    const url = server.publicDns.apply(dnsName => `https://${dnsName}`);
    
    exports.InstanceUrl = url;
    
    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);
        }
    }
    
    # YAML does not have the Apply method, but you can access values directly.
    name: aws-ec2-instance-yaml
    runtime: yaml
    description: An example that shows how to create an EC2 instance and security group.
    resources:
      server:
        type: aws:ec2:Instance
        properties:
          ami: ami-0319ef1a70c93d5c8
          instanceType: t2.micro
    outputs:
      InstanceUrl: https://${server.publicDns}
    

    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.

    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:

    "use strict";
    const pulumi = require("@pulumi/pulumi");
    const aws = require("@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
    exports.bucketName = bucket.id;
    
    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 };
    });
    

    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:

    "use strict";
    const pulumi = require("@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.jsonParse(jsonIAMPolicy).apply(policy => {
        // Empty the policy's Statements list.
        policy.Statement = [];
        return policy;
    });
    
    // Export the modified policy.
    exports.policy = policyWithNoStatements;
    
    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"] = Output.Create(new List<Dictionary<string, object?>> { });
                return policy;
            });
    
        // Export the modified policy.
        return new Dictionary<string, object?> { ["policy"] = policyWithNoStatements, };
    });
    

    For more details view the Go documentation.

    For more details view the .NET documentation.

      Neo just got smarter about infrastructure policy automation