1. Docs
  2. Concepts
  3. Inputs & outputs
  4. Accessing multiple outputs with All

Accessing multiple outputs with All

    If you need to access and use multiple outputs together, the all function acts like an apply across many resources, allowing you to retrieve and use multiple outputs at the same time. The all function waits for all output values to become available and then provides them as plain values to the apply apply Apply Apply function.

    This can be used to compute an entirely new output value, such as creating a new string by adding or concatenating outputs from two different resources together, or by creating a new data structure that uses their values. Just like with apply, the result of all is itself an Output.

    Creating a new string

    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.

    To demonstrate, let’s say you have created a server resource and a database resource, and their output values are as follows:

    # Example outputs for the server resource
    {
        "name": "myDbServer",
        "ipAddress": "10.0.0.0/24"
    }
    
    # Example outputs for the database resource
    {
        "name": "myExampleDatabase",
        "engine": "sql-db"
    }
    

    From the outputs of these resources, you want to create a database connection string that uses the following format:

    Server=tcp:<YourServerName>.database.windows.net,initial catalog=<YourDatabaseName>;
    

    In the following example, you provide the name of the server and the name of the database as arguments to all(). Those arguments are made available to the apply apply Apply Apply function and subsequently used to create the database connection string:

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

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

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

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

    from pulumi import Output
    # ...
    connection_string = Output.all(server=sql_server.name, db=database.name) \
        .apply(lambda args: f"Server=tcp:{args['server']}.database.windows.net;initial catalog={args['db']};")
    
    // ...
    connectionString := pulumi.All(sqlServer.Name, database.Name).ApplyT(
        func (args []interface{}) pulumi.StringOutput  {
            server := args[0]
            db := args[1]
            return pulumi.Sprintf("Server=tcp:%s.database.windows.net;initial catalog=%s;", server, db)
        },
    )
    
    //...
    
    // When all the input values have the same type, Output.All can be used and produces an ImmutableArray.
    var connectionString = Output.All(sqlServer.name, database.name)
        .Apply(t => $"Server=tcp:{t[0]}.database.windows.net;initial catalog={t[1]};");
    
    // For more flexibility, 'Output.Tuple' is used so that each unwrapped value will preserve their distinct type.
    var connectionString2 = Output.Tuple(sqlServer.name, database.name)
        .Apply(t => $"Server=tcp:{t.Item1}.database.windows.net;initial catalog={t.Item2};");
    
    // Or using a more natural Tuple syntax and a statement lambda expression.
    var connectionString3 = Output.Tuple(sqlServer.name, database.name).Apply(t =>
    {
        var (serverName, databaseName) = t;
        return $"Server=tcp:{serverName}.database.windows.net;initial catalog={databaseName};";
    });
    
    // ...
    
    // When all the input values have the same type, Output.all can be used
    var connectionString = Output.all(sqlServer.name(), database.name())
            .applyValue(t -> String.format("Server=tcp:%s.database.windows.net;initial catalog=%s;", t.get(0), t.get(1));
    
    // For more flexibility, 'Output.tuple' is used so that each unwrapped value will preserve their distinct type.
    var connectionString2 = Output.tuple(sqlServer.name, database.name)
            .applyValue(t -> String.format("Server=tcp:%s.database.windows.net;initial catalog=%s;", t.t1, t.t2));
    

    YAML does not have the Apply or All functions. Instead, you can access property values directly.

    variables:
      connectionString: Server=tcp:${sqlServer.name}.database.windows.net;initial catalog=${database.name};
    

    The all function works by returning an output that represents the combination of multiple outputs. Based on the example output values provided above, the final value of the generated connection string will resemble the following:

    Server=tcp:myDbServer.database.windows.net;initial catalog=myExampleDatabase;
    

    Using string interpolation

    There is an easier way to generate a concatenated string value using multiple outputs, and that is by using interpolation. Pulumi exposes interpolation helpers that enables you to create strings that contain outputs. These interpolation methods wrap all and apply with an interface that resembles your language’s native string formatting functions. The example below demonstrates how to create a URL from the hostname and port output values of a web server.

    "use strict";
    const pulumi = require("@pulumi/pulumi");
    const aws = require("@pulumi/aws");
    
    const bucket = new aws.s3.Bucket("bucket");
    
    const file = new aws.s3.BucketObject("bucket-object", {
        bucket: bucket.id,
        key: "some-file.txt",
        content: "some-content",
    });
    
    // concat takes a list of args and concatenates all of them into a single output:
    exports.s3Url1 = pulumi.concat("s3://", bucket.bucket, "/", file.key);
    
    // interpolate takes a JavaScript template literal and expands outputs correctly:
    exports.s3Url2 = pulumi.interpolate`s3://${bucket.bucket}/${file.key}`;
    
    import * as pulumi from "@pulumi/pulumi";
    import * as aws from "@pulumi/aws";
    
    const bucket = new aws.s3.Bucket("bucket");
    
    const file = new aws.s3.BucketObject("bucket-object", {
        bucket: bucket.id,
        key: "some-file.txt",
        content: "some-content",
    });
    
    // concat takes a list of args and concatenates all of them into a single output:
    export const s3Url1: pulumi.Output<string> = pulumi.concat("s3://", bucket.bucket, "/", file.key);
    
    // interpolate takes a JavaScript template literal and expands outputs correctly:
    export const s3Url2: pulumi.Output<string> = pulumi.interpolate`s3://${bucket.bucket}/${file.key}`;
    
    import pulumi
    import pulumi_aws as aws
    
    bucket = aws.s3.Bucket("bucket")
    
    file = aws.s3.BucketObject("bucket-object",
        bucket=bucket.id,
        key="some-file.txt",
        content="some-content",
    )
    
    # concat takes a list of args and concatenates all of them into a single output:
    s3Url1 = pulumi.Output.concat("s3://", bucket.bucket, "/", file.key)
    
    # format takes a template string and a list of args or keyword args and formats the string, expanding outputs correctly:
    s3Url2 = pulumi.Output.format("s3://{0}/{1}", bucket.bucket, file.key)
    
    package main
    
    import (
    	"github.com/pulumi/pulumi-aws/sdk/v4/go/aws/s3"
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    		bucket, err := s3.NewBucket(ctx, "bucket", nil)
    		if err != nil {
    			return err
    		}
    
    		file, err := s3.NewBucketObject(ctx, "bucket-object", &s3.BucketObjectArgs{
    			Bucket:  bucket.ID(),
    			Key:     pulumi.String("some-file.txt"),
    			Content: pulumi.String("some-content"),
    		})
    		if err != nil {
    			return err
    		}
    
    		s3Url := pulumi.Sprintf("s3://%s/%s", bucket.ID(), file.Key)
    		ctx.Export("s3Url", s3Url)
    
    		return nil
    	})
    }
    
    using System.Collections.Generic;
    using Pulumi;
    using Pulumi.Aws.S3;
    
    return await Deployment.RunAsync(() => 
    {
        var bucket = new Bucket("bucket");
    
        var file = new BucketObject("bucket-object", new BucketObjectArgs
        {
            Bucket = bucket.Id,
            Key = "some-file.txt",
            Content = "some-content",
        });
    
        var s3Url = Output.Format($"s3://{bucket.Id}/{file.Key}");
    
        return new Dictionary<string, object?>
        {
            ["s3Url"] = s3Url,
        };
    });
    
    package myproject;
    
    import com.pulumi.Context;
    import com.pulumi.Pulumi;
    import com.pulumi.aws.s3.Bucket;
    import com.pulumi.aws.s3.BucketObject;
    import com.pulumi.aws.s3.BucketObjectArgs;
    import com.pulumi.core.Output;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(App::stack);
        }
    
        public static void stack(Context ctx) {
            var bucket = new Bucket("bucket");
    
            var file = new BucketObject("bucket-object", BucketObjectArgs.builder()
                .bucket(bucket.id())
                .key("some-file.txt")
                .content("some-content")
                .build());
    
            var s3Url = Output.format("s3://%s/%s", bucket.bucket(), file.key());
    
            ctx.export("s3Url", s3Url);
        }
    }
    
    name: aws-s3bucket-bucketobject-interpolate-yaml
    runtime: yaml
    description: An example that creates an S3 bucket and bucket object
    
    resources:
      bucket:
        type: aws:s3/bucket:Bucket
        name: bucket
    
      bucketObject:
        type: aws:s3/bucketObject:BucketObject
        name: bucket-object
        properties:
          bucket: ${bucket.id}
          key: some-file.txt
          content: some-content
    
    outputs:
      s3Url: s3://${bucket.id}/${bucketObject.key}
    

    You can use string interpolation to do things like export a stack output or provide a dynamically computed string as a new resource argument.

    Creating a new data structure

    In addition to strings, the all function can also be used to create new data structures such as:

    • Lists | Arrays | Slices
    • Dicts | Objects | Maps

    Using the same example server and database resources and their corresponding output values, you can see this demonstrated in the example below:

    const pulumi = require("@pulumi/pulumi");
    // ...
    
    // An example of creating a new Output of type Object
    const connectionDetails = pulumi.all([sqlServer.ipAddress, database.port])
        .apply(([serverIp, databasePort]) => ({
            server_ip: serverIp,
            database_port: databasePort,
        })
    );
    
    // An example of creating a new Output of type Array
    const connectionDetails = pulumi.all([server.ipAddress, database.port])
        .apply(([ip, port]) => [ip, port]);
    
    import * as pulumi from "@pulumi/pulumi";
    // ...
    
    // An example of creating a new Output of type Dictionary
    let connectionDetails = pulumi.all([server.ipAddress, database.port])
        .apply(([ip, port]) => {
            return {
                serverIp: ip,
                databasePort: port,
            };
        }
    );
    
    // An example of creating a new Output of type Array
    let connectionDetails = pulumi.all([server.ipAddress, database.port])
        .apply(([ip, port]) => {
            return [ip, port];
        }
    );
    
    from pulumi import Output
    # ...
    
    # An example of creating an Output of type Dict
    connection_details = Output.all(sql_server.ipAddress, database.port) \
        .apply(lambda args: {
            "server_ip": args[0],
            "database_port": args[1]
        })
    
    # An example of creating an Output of type List
    connection_details = Output.all(sql_server.ipAddress, database.port) \
        .apply(lambda args: [args[0], args[1]])
    
    // ...
    
    // An example of creating an Output of type Map
    connectionDetails := pulumi.All(sqlServer.IpAddress, database.Port).ApplyT(
        func(args []interface{}) map[string]interface{} {
        	ipAddress := args[0].(string)
    		port := args[1].(string)
        	return map[string]interface{}{
        		"server_ip":     ipAddress),
        		"database_port": port,
        	}
        }
    )
    
    // An example of creating an Output of type Array
    connectionDetails := pulumi.All(sqlServer.IpAddress, database.Port).ApplyT(
        func(args []interface{}) []interface{} {
    		return args
    	}).(pulumi.ArrayOutput)
    
    //...
    
    // An example of creating an Output of type Dictionary
    var connectionDetails = Output.All(sqlServer.IpAddress, database.Port).Apply(values =>
    {
        var ipAddress = values[0];
        var port = values[1];
        return new Dictionary<string, object>
        {
            {"serverIp", ipAddress},
            {"port", port}
        };
    });
    
    // An example of creating an Output of type List
    var connectionDetails = Output.All(sqlServer.IpAddress, database.Port).Apply(values =>
    {
        var ipAddress = values[0];
        var port = values[1];
        return new List<object> { ipAddress, port };
    });
    
    // ...
    
    // An example of creating an Output of type Map
    var connectionDetails = Output.tuple(sqlServer.ipAddress(), database.port())
        .applyValue(t -> Map.of("ServerIp", t.t1, "DatabasePort", t.t2));
    
    // An example of creating an Output of type List
    var connectionDetails2 = Output.tuple(sqlServer.ipAddress(), database.port())
        .applyValue(t -> List.of(t.t1, t.t2));
    

    YAML does not have the Apply or All functions. Instead, you can access property values directly.

    This example is not applicable in Pulumi YAML.
    

    Creating a JSON object

    You can also create JSON objects using multiple output values in Pulumi. Doing so requires the use of apply or one of Pulumi’s JSON-specific helpers.

    "use strict";
    const pulumi = require("@pulumi/pulumi");
    const aws = require("@pulumi/aws");
    
    const contentBucket = new aws.s3.Bucket("content-bucket", {
        acl: "private",
        website: {
            indexDocument: "index.html",
            errorDocument: "index.html",
        },
        forceDestroy: true,
    });
    
    const originAccessIdentity = new aws.cloudfront.OriginAccessIdentity("cloudfront", {
        comment: pulumi.interpolate`OAI-${contentBucket.bucketDomainName}`,
    });
    
    new aws.s3.BucketPolicy("cloudfront-bucket-policy", {
        bucket: contentBucket.bucket,
        policy: pulumi.all([contentBucket.bucket, originAccessIdentity.iamArn]).apply(([bucketName, iamArn]) =>
            JSON.stringify({
                Version: "2012-10-17",
                Statement: [
                    {
                        Sid: "CloudfrontAllow",
                        Effect: "Allow",
                        Principal: {
                            AWS: iamArn,
                        },
                        Action: "s3:GetObject",
                        Resource: `arn:aws:s3:::${bucketName}/*`,
                    },
                ],
            }),
        ),
    });
    
    import * as pulumi from "@pulumi/pulumi";
    import * as aws from "@pulumi/aws";
    
    const contentBucket = new aws.s3.Bucket("content-bucket", {
        acl: "private",
        website: {
            indexDocument: "index.html",
            errorDocument: "index.html",
        },
        forceDestroy: true,
    });
    
    const originAccessIdentity = new aws.cloudfront.OriginAccessIdentity("cloudfront", {
        comment: pulumi.interpolate`OAI-${contentBucket.bucketDomainName}`,
    });
    
    // apply method
    new aws.s3.BucketPolicy("cloudfront-bucket-policy", {
        bucket: contentBucket.bucket,
        policy: pulumi.all([contentBucket.bucket, originAccessIdentity.iamArn]).apply(([bucketName, iamArn]) =>
            JSON.stringify({
                Version: "2012-10-17",
                Statement: [
                    {
                        Sid: "CloudfrontAllow",
                        Effect: "Allow",
                        Principal: {
                            AWS: iamArn,
                        },
                        Action: "s3:GetObject",
                        Resource: `arn:aws:s3:::${bucketName}/*`,
                    },
                ],
            }),
        ),
    });
    
    import pulumi
    import pulumi_aws as aws
    import json
    
    bucket = aws.s3.Bucket(
        "content-bucket",
        acl="private",
        website=aws.s3.BucketWebsiteArgs(
            index_document="index.html", error_document="404.html"
        ),
    )
    
    origin_access_identity = aws.cloudfront.OriginAccessIdentity(
        "cloudfront",
        comment=pulumi.Output.concat("OAI-", bucket.id),
    )
    
    bucket_policy = aws.s3.BucketPolicy(
        "cloudfront-bucket-policy",
        bucket=bucket.bucket,
        policy=pulumi.Output.all(
            cloudfront_iam_arn=origin_access_identity.iam_arn,
            bucket_arn=bucket.arn
        ).apply(
            lambda args: json.dumps(
                {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "CloudfrontAllow",
                            "Effect": "Allow",
                            "Principal": {
                                "AWS": args["cloudfront_iam_arn"],
                            },
                            "Action": "s3:GetObject",
                            "Resource": f"{args['bucket_arn']}/*",
                        }
                    ],
                }
            )
        ),
        opts=pulumi.ResourceOptions(parent=bucket)
    )
    /*
    The Pulumi Go SDK does not currently support serializing or deserializing maps with unknown values.
    It is tracked here: https://github.com/pulumi/pulumi/issues/12460
    
    The following is a simplified example of using `pulumi.JSONMarshal` in Go.
    */
    
    package main
    
    import (
    	"encoding/json"
    	"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/cloudfront"
    	"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    func main() {
    	pulumi.Run(func(ctx *pulumi.Context) error {
    		bucket, err := s3.NewBucket(ctx, "content-bucket", &s3.BucketArgs{
    			Acl: pulumi.String("private"),
    			Website: &s3.BucketWebsiteArgs{
    				IndexDocument: pulumi.String("index.html"),
    				ErrorDocument: pulumi.String("404.html"),
    			},
    		})
    		if err != nil {
    			return err
    		}
    
    		originAccessIdentity, err := cloudfront.NewOriginAccessIdentity(ctx, "cloudfront", &cloudfront.OriginAccessIdentityArgs{
    			Comment: pulumi.Sprintf("OAI-%s", bucket.ID()),
    		})
    		if err != nil {
    			return err
    		}
    
    		_, err = s3.NewBucketPolicy(ctx, "cloudfront-bucket-policy", &s3.BucketPolicyArgs{
    			Bucket: bucket.ID(),
    			Policy: pulumi.All(bucket.Arn, originAccessIdentity.IamArn).ApplyT(
    				func(args []interface{}) (pulumi.StringOutput, error) {
    					bucketArn := args[0].(string)
    					iamArn := args[1].(string)
    
    					policy, err := json.Marshal(map[string]interface{}{
    						"Version": "2012-10-17",
    						"Statement": []map[string]interface{}{
    							{
    								"Sid":    "CloudfrontAllow",
    								"Effect": "Allow",
    								"Principal": map[string]interface{}{
    									"AWS": iamArn,
    								},
    								"Action":   "s3:GetObject",
    								"Resource": bucketArn + "/*",
    							},
    						},
    					})
    
    					if err != nil {
    						return pulumi.StringOutput{}, err
    					}
    					return pulumi.String(policy).ToStringOutput(), nil
    				}).(pulumi.StringOutput),
    		}, pulumi.Parent(bucket))
    		if err != nil {
    			return err
    		}
    		return nil
    	})
    }
    
    using Pulumi;
    using Pulumi.Aws.S3;
    using Pulumi.Aws.S3.Inputs;
    using Pulumi.Aws.Iam;
    using Pulumi.Aws.CloudFront;
    using System.Collections.Generic;
    using System.Text.Json;
    
    return await Deployment.RunAsync(() =>
    {
        var bucket = new Bucket("content-bucket", new()
        {
            Acl = "private",
            Website = new BucketWebsiteArgs
            {
                IndexDocument = "index.html",
                ErrorDocument = "404.html",
            },
        });
        
        var originAccessIdentity = new OriginAccessIdentity("cloudfront", new OriginAccessIdentityArgs
        {
            Comment = Output.Format($"OAI-{bucket.Id}"),
        });
        
        var bucketPolicy = new BucketPolicy("cloudfront-bucket-policy", new BucketPolicyArgs
        {
            Bucket = bucket.BucketName,
            Policy = Output.Tuple(bucket.Arn, originAccessIdentity.IamArn)
            .Apply(t =>
            {
                string bucketArn = t.Item1;
                string cloudfrontIamArn = t.Item2;
        
                var policy = new
                {
                    Version = "2012-10-17",
                    Statement = new object[]
                    {
                        new
                        {
                            Sid = "CloudfrontAllow",
                            Effect = "Allow",
                            Principal = new { AWS = cloudfrontIamArn },
                            Action = "s3:GetObject",
                            Resource = $"{bucketArn}/*",
                        },
                    },
                };
        
                return JsonSerializer.Serialize(policy);
            }),
        }, new CustomResourceOptions { Parent = bucket });
    });
    
    package myproject;
    
    import com.pulumi.Pulumi;
    import com.pulumi.core.Output;
    import com.pulumi.aws.s3.Bucket;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(ctx -> {
                var bucket = new Bucket("my-bucket");
                ctx.export("bucketName", bucket.bucket());
            });
        }
    }
    
    # In Pulumi YAML, you can access the values of outputs directly.
    name: aws-s3websitebucket-oai-bucketpolicy-yaml
    runtime: yaml
    description: An example that creates a website S3 bucket, a CloudFront Origin Access Identity, and a bucket policy.
    resources:
      content-bucket:
        type: aws:s3:Bucket
        properties:
          acl: private
          website:
            indexDocument: index.html
            errorDocument: index.html
          forceDestroy: true
    
      cloudfront-origin-access-identity:
        type: aws:cloudfront:OriginAccessIdentity
        properties:
          comment: OAI-${content-bucket.bucketDomainName}
    
      cloudfront-bucket-policy:
        type: aws:s3:BucketPolicy
        properties:
          bucket: ${content-bucket.id}
          policy: ${cloudfrontAccessPolicy.json}
    
    variables:
      cloudfrontAccessPolicy:
        fn::invoke:
          Function: aws:iam:getPolicyDocument
          Arguments:
            statements:
              - principals:
                  - type: AWS
                    identifiers:
                      - ${cloudfront-origin-access-identity.iamArn}
                actions:
                  - s3:GetObject
                resources:
                  - ${content-bucket.arn}/*
    
      Introducing Drift Detection, TTL Stacks, and Scheduled Deployments. Learn More.