Inputs, Outputs & apply
The async value model that trips everyone.
The one concept that trips every Pulumi newcomer is Inputs and Outputs. Many resource attributes are not known until the resource is actually created — an ARN, an assigned IP, a generated ID. Pulumi models these as Output<T>: a value that will exist after deployment, not during program execution. You cannot treat an Output like a plain string, because at the moment your code runs, the real value does not exist yet.
const bucket = new aws.s3.BucketV2("logs", {});// WRONG: bucket.arn is an Output<string>, not a string// const msg = "ARN is " + bucket.arn; // => "ARN is Calculating..."// RIGHT: transform the value once it existsconst msg = bucket.arn.apply(arn => `ARN is ${arn}`);// or interpolate several Outputs:export const url = pulumi.interpolate`https://${bucket.bucketDomainName}`;
apply, interpolate, all
Three tools cover almost every case. apply(fn) runs your function once the Output resolves and returns a new Output. pulumi.interpolate is a template string that understands Outputs. pulumi.all([...]) waits for several Outputs together so you can combine them. The mental rule: once a value is an Output, everything downstream that touches it is also an Output until you export it or feed it into another resource.
const combined = pulumi.all([bucket.arn, queue.url]).apply(([arn, url]) => buildPolicyJson(arn, url)); // both resolved together