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

best-summer-38252

02/22/2023, 11:52 PM
This example shows a gcp service account name (an Output<string>) being used directly. https://www.pulumi.com/registry/packages/gcp/api-docs/serviceaccount/iambinding/#google_service_account_iam_policy But in my use, I get `Error 400: The member Calling [toString] on an [Output<T>] is not supported.`on the appIAM's serviceAccountId.
Copy code
const appServiceAccount = new gcp.serviceaccount.Account(camelcase(APP_NAME + "-SA"), {
  accountId: kebabcase(APP_NAME),
  displayName: startcase(APP_NAME),
});

const appAuthz = gcp.organizations.getIAMPolicy({
  bindings: [
    {
      role: "roles/workflows.invoker",
      members: [`${appServiceAccount.email}`],
    },
  ],
});

const appIAM = new gcp.serviceaccount.IAMPolicy("appSAIAM", {
  serviceAccountId: appServiceAccount.name,
  policyData: appAuthz.then((admin) => admin.policyData),
});
Ive tried, serviceAccountId: appServiceAccount.name.apply((n) =>
${n}
) and ``${appServiceAccount.name}`` etc. What am I missing?
l

little-cartoon-10569

02/22/2023, 11:56 PM
The service account name isn't being used directly. It's being used as a lifted output. The Pulumi resource (IAMPolicy) knows that
appServiceAccount.name
is an output, and handles it accordingly. You have to do the same.
If you're passing this sort of thing to a Pulumi resource constructor, you don't have to do anything: Pulumi does it for you. Otherwise, you need to use the value inside the apply. For example, you can't do this:
Copy code
console.log(appServiceAccount.name.apply((n) => `${n}`)
You need to do your work inside the apply:
Copy code
appServiceAccount.name.apply((n) => console.log(`${n}`))
The value returned from an apply is always another output, so you can't just print it out or access the value. This is because your code is running before the value is known to be available. Putting your code inside the apply ensure that the value is available for your code to use.
b

best-summer-38252

02/23/2023, 12:03 AM
so the ref example in the docs is wrong?
l

little-cartoon-10569

02/23/2023, 12:24 AM
It looks good to me. Where are you thinking might have an issue?
b

best-summer-38252

02/23/2023, 12:43 AM
Error 400: The member Calling [toString] on an [Output<T>] is not supported
My code
Copy code
const appIAM = new gcp.serviceaccount.IAMPolicy("appSAIAM", {
  serviceAccountId: appServiceAccount.name,
  policyData: appAuthz.then((admin) => admin.policyData),
});
The example's code:
Copy code
const admin_account_iam = new gcp.serviceaccount.IAMPolicy("admin-account-iam", {
    serviceAccountId: sa.name,
    policyData: admin.then(admin => admin.policyData),
});
The error says its the IAMPolicy but if I hard code the binding then I get a different set of errors so I am thinking its not the serviceAccountId value at all.
l

little-cartoon-10569

02/23/2023, 1:05 AM
It may well be admin.policyData. appAuthz is a Promise, so
admin.then(...)
is returning a Promise. I'm guessing that appAuthz's policyData property is an Output? If it is, then you're setting the policyData property to be a value of type
Promise<Output<string>
. I'm not certain on this, but I think that Pulumi's magic lifting capabilities don't work on Promises, so the value resolved for policyData is of type
Output<string>
, instead of
string
. If I'm right, the best fixes are either to not use a Promise at all (change to an Input or Output), or change
appAuthz.policyData
to return a string. Assuming that this latter is impossible (it probably is), then the easiest fix is this:
Copy code
const appIAM = new gcp.serviceaccount.IAMPolicy("appSAIAM", {
  serviceAccountId: appServiceAccount.name,
  policyData: pulumi.output(appAuthz).policyData,
});
Assuming that works, the example should be updated to this. Even if there is a difference elsewhere in your code that is causing the problem for you, this code is more robust and easier to read than the example code.
b

best-summer-38252

02/23/2023, 1:11 AM
I get the thinking behind appy vs then but its a pita when its mixed up. Your example of Pulumi input not accepting the promise from appAuthz. Another example is gcp.compute.getDefaultServiceAccount() returns a promise but gcp.serviceaccount.Account() is an Output. So feature flagging real prob.
l

little-cartoon-10569

02/23/2023, 1:20 AM
Those get...() functions are exposing the gcp API. They're not designed to work with Pulumi properly, so there is clunkiness involved. 😞
Wrapping promises in an output is a very handy thing to do in Pulumi. It enables the magic powers of lifting.
b

best-summer-38252

02/23/2023, 1:27 AM
Like if you create a Firestore db with Pulumi then your broken. Firestore can not be deleted and Pulumi will fail on a rename or destroy. The everything is CRUD inherently from Terrafrom is inherently flawed. The wrapping with output is something I had not realized before.
Not a problem with
gcp.serviceaccount.IAMPolicy
but the
getIAMPolicy
Copy code
const appAuthz = gcp.organizations.getIAMPolicy({
  bindings: [
    {
      role: "roles/workflows.invoker",
      members: appServiceAccount.email.apply(email => [`serviceAccount:${email}`]),
    },
  ],
});
And I cant just do the entire bindings array in the apply() scope nor whole args object:
Copy code
const appAuthz = gcp.organizations.getIAMPolicy(
  appServiceAccount.email.apply(email => ({
    bindings: [{
      role: "roles/workflows.invoker",
      members:  [`serviceAccount:${email}`]
    }]
  }))
);
I cant find an example where the service account is not hard coded https://www.pulumi.com/registry/packages/gcp/api-docs/organizations/getiampolicy/
l

little-cartoon-10569

02/23/2023, 8:52 PM
Yes, for the get functions, which are not Pulumi resources, you cannot do this. Those API calls are done at run time, not at deploy time. And applied values are not known until deploy time.
So you need to run the getIAMPolicy code inside the apply call, if you need access to something that isn't available until deploy time.
So change this:
Copy code
const appAuthz = gcp.organizations.getIAMPolicy(
  appServiceAccount.email.apply(email => ({
    bindings: [{
      role: "roles/workflows.invoker",
      members:  [`serviceAccount:${email}`]
    }]
  }))
);
To this:
Copy code
const appAuthz = appServiceAccount.email.apply(email => {
  return gcp.organizations.getIAMPolicy(
   {
    bindings: [{
      role: "roles/workflows.invoker",
      members:  [`serviceAccount:${email}`]
    }]
  });
});
(Or something like that; code not checked, compiled, linted or anythiing 🙂 )
2 Views