https://pulumi.com logo
#typescript
Title
# typescript
a

acceptable-army-69872

07/16/2021, 6:11 PM
I'm trying to do something to get a "Map" of providers that I can use for populating other resource creation calls. The provider creation seems to work, but the Map stays empty and I can't use the providers anywhere.
Copy code
function createProviderMap(o_accounts: aws.organizations.Account[]): Map <string, aws.Provider> {
  let providerMap = new Map();

  o_accounts.map( o_account => {
    pulumi.all([o_account.id, o_account.name ]).apply ( ([id, name]) => {
      providerMap.set(name, new aws.Provider(`${id}-provider`, {
                    assumeRole: {
                        roleArn: `arn:aws:iam::${id}:role/OrganizationAccountAccessRole`,
                        sessionName: "PulumiSession",
                    },
                    profile: config_aws.require('profile'),
                    region: aws.config.requireRegion(),
                }));
    });
  });
  console.log(providerMap);
}
I feel like I'm missing something big, but this is my first use of Map. Perhaps the issue is I'm creating the provider inside of a
.all
...thoughts?
Like a solution could be to just use the original array, and run "finds" on it every time I need to get a provider, but that feels icks.
s

steep-toddler-94095

07/16/2021, 8:23 PM
you can't populate the
Map
within an
apply
like that. Your function would have to return a
Copy code
pulumi.Output<{
    [x: string]: aws.Provider;
}>[]
but then the
Output
would sort of taint your resource types in a similar way that
Promise
does. There isn't really a way around having to deal with
Output
as long as you want to get the
id
from an
aws.organizations.Account[]
a

acceptable-army-69872

07/16/2021, 8:37 PM
Let me step back and ask what the "clever way" to deal with this is?
object per account representation? and a getProvider method? Maybe that method populates this.provider if undefined?
What I'm trying to do is operate on a big ass json blob of accounts and what I want their configurations to be (which roles added, which control tower settings enabled).
or something like that. so it's one codebase to many aws accounts.
s

steep-toddler-94095

07/16/2021, 8:44 PM
what about adding the account id/name as part of your json blob configuration so it doesn't need to be looked up during runtime?
a

acceptable-army-69872

07/16/2021, 8:50 PM
The account id is only in the account blob if it's an older account.
So if an account doen't exist, it creates it, and adds it to the org.
it's really only the "org" object that has most of the information. I'm assuming using an apply from a getAccount to help has the same issues.
s

steep-toddler-94095

07/16/2021, 9:05 PM
you'll run into a similar issue, except the
getResource
functions return a
Promise
instead of an
Output
. Given the requirement that you create some of these accounts dynamically I think you may just have to deal with having `Output<Provider>`s. I don't recommend using
getOrganization
here because the promise may resolve before the new account is actually created. The official stance is that it's not recommended to create resources within an
apply
but sometimes I do not see a better way around it.
👍 1
a

acceptable-army-69872

07/16/2021, 9:13 PM
I'll see what I can do to work around it. I appreciate your insight.
when I say work around, I mean, adjust my approach.
I think I may go down the path of creating an "account" base class, and then instantiating one per account. I can have it do things like,
if this.accountId === undefined; this.accountId = createAccount(name, email);
so instead of big ass json blob, big ass directory of account objects.
for some reasons I feel like the problems will be smaller and less systemic doing it that way.
I've done enough stuff like
Copy code
export function getOrCreateZone(zoneName: string): any {
  return aws.route53.getZone({
    name: zoneName
  }).then(zone => {
    let zoneId: any  = zone.zoneId;
    if (zoneId === undefined) {
      const newZone: aws.route53.Zone = new aws.route53.Zone(zoneName, {
        comment: `${config.require("stack")} ${zoneName} service`,
        name: zoneName,
        tags: {
          Name: zoneName,
          Environment: config.require("stack")
        }
      });

      zoneId = newZone.zoneId.apply(zId => {return `${zId}`});
    }
    return zoneId;
  });
}
that I think it would work.
s

steep-toddler-94095

07/16/2021, 9:30 PM
unrelated, but I'd highly suggest NOT using
any
. At the very least, let Typescript infer the type for you instead
Copy code
export function getOrCreateZone(zoneName: string) {
  return aws.route53.getZone({ name: zoneName }).then(
    (zone) =>
      (zone.zoneId as string | undefined) ??
      new aws.route53.Zone(zoneName, {
        comment: `${config.require("stack")} ${zoneName} service`,
        name: zoneName,
        tags: {
          Name: zoneName,
          Environment: config.require("stack"),
        },
      }).zoneId
  )
}
a

acceptable-army-69872

07/16/2021, 9:36 PM
Fair point. The step after "getting it to work" really should be cleaning stuff like any's up.