# general


11/14/2023, 9:05 PM
hi! i had a question, when does Pulumi actually run the query for a
type of operation? is it run at the beginning of the
operation or is it deferred until the value is actually needed? in this scenario, we are calling
after a thing that creates an IAM role so that we can use that role in an IAM policy, but we get
the first time we run Pulumi which makes the IAM policy invalid. unfortunately, the role is created as a side effect of the aws.ssoadmin.AccountAssignment resource and that resource does not return info about the role created, so we don't have an explicit way to get the role. we tried adding an explicit
for the policy to depend on the AccountAssignment, and it does end up updating the policy after the AccountAssignment is created, but it still ends up trying to use the
value and fails. a second
pulumi up
fixes the issue but it's annoying and feels wrong to have to do this. I would expect if the query is deferred until it's actually used, then this should succeed on the first try given that there is an explicit dependency, but it also could be that the role is created asynchronously some time after the AccountAssignment so it might just be unreliable in general.
i guess one potential approach might be to synchronously query for the role and loop until it gets a value, but this also seems fraught with peril
as a side note, i'm new to Pulumi and have several years experience with Terraform. in Terraform, i'm used to seeing some values with "(known after apply)", not sure if Pulumi has an equivalent. to me it's weird that the "preview" diff shows
right away, which makes me think that it's not waiting to compute the value until it's needed


11/14/2023, 9:34 PM
As you suggested, the role is created asynchronously on the aws side, so will be unreliable in general. Something you can do is use the promise form of
, and keep retrying until you end up with a non-null response. Let me setup an environment so I can provide an example quickly
@freezing-electrician-6256 can you provide an example of your current
usage, which will help with doing an example here
This is a generic example, there's likely cleaner ways to do it; such as wrapping the promise with this library:
Copy code
async function getRoleAsync(nameRegex: string): Promise<aws.iam.GetRoleResult> {
    let roleName: string | null = null;
    const maxRetries = 10;
    let attempts = 0;
    const sleepTime = 1000;
    do {
        let rolesResult = await aws.iam.getRoles({
            nameRegex: nameRegex,
        if (rolesResult.names.length > 0) {
            roleName = rolesResult.names[0];
        if (roleName == null) {
            console.log("retrying!", rolesResult);
            await new Promise(r => setTimeout(r, sleepTime));
    } while (roleName == null && attempts < maxRetries);
    if (roleName == null) {
        throw new Error("Failed to find role");

    return aws.iam.getRole({
        name: roleName,

// These are for demo purposes to show how to unrole a promise in Pulumi
const roleDependingOnResource = => getRoleAsync("NEVER_EXISTS"));
const roleNonDependent = pulumi.output(getRoleAsync("NEVER_EXISTS"));
alternate using that library:
Copy code
import { retryDecorator } from "ts-retry-promise";

const getRole = (nameRegex: string) => aws.iam.getRoles({
    nameRegex: nameRegex,
}).then(roles => {
    if (roles.names.length <= 0) {
        throw new Error("Failed to find role");
    return aws.iam.getRole({
        name: roles.names[0],
const retryGetRole = retryDecorator(getRole, { retries: 10, delay: 1000 });

// These are for demo purposes to show how to unrole a promise in Pulumi
const roleDependingOnResource = => retryGetRole("NEVER_EXISTS"));
const roleNonDependent = pulumi.output(retryGetRole("NEVER_EXISTS"));


11/14/2023, 11:07 PM
the usage is going into a big jsonstringify policy, looks something like:
Copy code
policy: pulumi.jsonStringify({
        Version: "2012-10-17",
        Statement: [
                Sid: "Foo",
                Effect: "Allow",
                Principal: {
                    AWS: [
(the getRoleOutput is technically wrapped in a function but effectively ends up like that)
hm okay, yeah i guess we can just wait for the role to become valid