Hey there, I'm trying to do something with Pulumi ...
# typescript
c
Hey there, I'm trying to do something with Pulumi and I feel like I am making this way more complicated than it needs to be and was wondering if anyone knows of a simpler solution. For context, I'm using the auth0 provider. The project I'm working on is supposed to be able to be deployed to
n
tenants. So I want to create some ClientGrants which for one of their fields require a
clientId
which I want to pull from Client objects also managed through Pulumi. After I have created all Clients I just have a simple array list of Clients, and when creating the grants I want to refer to the id of a specific Client in the list. Except when I just to something like
Copy code
const clientId = clients.find(client => client.name === 'Foo')!.clientId
This wouldn't work because
client.name
is of type
Output<string>
because I wouldn't know the client name. So my other attempt was using
output
to lift the type like this:
Copy code
const clientId = output(clients).apply((clients) => clients.find((client) => client.name === "Foo")!.clientId);
But that still has the same problem 😞 I'm a bit at a loss on the correct way to solve this and I wasn't able to find my problem in the Pulumi docs or on the interwebs
s
can you post a larger snippet of your code? maybe there's a better way to structure it to avoid dealing with these array outputs
c
Hmm I can try, I've been trying to keep the code somewhat "clean" meaning I try to separate the configuration part and the create pulumi objects part. The function that creates all clients, resourceservers and grants looks like this
Copy code
export async function setupApplications(
  auth0Provider: Auth0Provider,
  gcpProvider: GcpProvider,
  tenant: Tenant,
  project: Project,
  importResource: Importer,
) {
  const readSecret = secretAccessor(gcpProvider, "big-hero-0");

  const clients: Client[] = await setupClients(auth0Provider, readSecret, tenant, project, importResource);

  const resourceServers: ResourceServer[] = await setupResourceServers(auth0Provider, readSecret, project, importResource);

  const grants: ClientGrant[] = await setupClientGrants(
    auth0Provider,
    readSecret,
    tenant,
    project,
    importResource,
    clients,
    resourceServers,
  );
}
the setupClients and setupResourceServers both look like this
Copy code
async function setupClients(
  auth0Provider: Auth0Provider,
  readSecret: ReadSecret,
  tenant: Tenant,
  project: Project,
  importResource: Importer,
) {
  const definitions = await getAuth0ClientsForTenant(readSecret, tenant, project);
  return await Promise.all(
    definitions.map(
      async (client) =>
        new Client(client.name, client, {
          provider: auth0Provider,
          import: await importResource("client", client.name),
          // We never want to delete clients since this would destroy client id / secret, so protect them.
          protect: true,
        }),
    ),
  );
}
'definitions' are typescript types that modify the Pulumi auth0
RandomResourceTypeArgs
types to have a 'static' (non-output) name that is identifiable across tenants so that those can be used for importing existing resources
FYI I found a way to fix this. I started doing this pattern where, when I create resources, I create maps containing their
string
name and
Output<string>
id. Looks a bit like this
Copy code
async function setupResourceServers(
  auth0Provider: Auth0Provider,
  readSecret: ReadSecret,
  project: Project,
  importResource: Importer,
) {
  const definitions = await getAuth0Apis(readSecret, project);
  const entries = await Promise.all(
    definitions.map(async (resourceServer) => {
      const res = new ResourceServer(resourceServer.name, resourceServer, {
        provider: auth0Provider,
        import: await importResource("resource-server", resourceServer.name),
        // We never want to delete resource servers
        protect: true,
      });
      return [resourceServer.name, res.identifier] as const;
    }),
  );
  return new Map(entries);
}
So when I create resources depending on others, I can pass through those maps to access their id's 😄
s
glad you found a solution. want to point out though that in some cases it might be best to create resources adjacent to each other, e.g. start with an array of data and then create and return the client, resourceServer, and clientGrant within the
.map
so in the end you have an
Array<{client: Client, resourceServer: ResourceServer, clientGrant: ClientGrant}>
c
Yeah agreed. I tried to make the structure of the project align with how the auth0 management dashboard is organised so you could "easily" find what you need