Can't use runtime generated role for a Provider
I have a usecase where I need to generate an IAM Role and then use it as a provider to generate more AWS resources.
When trying the example below, the first pulumi up always fails since Pulumi tries to assume the role immediately after creation.
import * as aws from "@pulumi/aws";
const role = new aws.iam.Role("testrole", {
assumeRolePolicy: aws.getCallerIdentity().then(id => {
return {
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: {
AWS: `arn:aws:iam::${id.accountId}:root`
},
Action: "sts:AssumeRole"
}
]
};
})
});
const rolePolicy = new aws.iam.RolePolicy("test", {
role: role,
policy: {
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Action: "s3:*",
Resource: "*"
}
]
}
});
const roleProvider = new aws.Provider(
"test",
{
assumeRole: {
roleArn: role.arn
}
},
{ dependsOn: rolePolicy }
);
// try to make an s3 bucket using role provider
// this will fail once and then work
const s3Bucket = new aws.s3.Bucket(
"test",
{},
{ provider: roleProvider, dependsOn: roleProvider }
);
Error output when trying to apply
Do you want to perform this update? yes
Updating (dev):
Type Name Status Info
+ pulumi:pulumi:Stack tests-dev created
+ ├─ aws:iam:Role testrole created
+ ├─ aws:iam:RolePolicy test created
+ ├─ pulumi:providers:aws test created
└─ aws:s3:Bucket test **failed** 1 error
Diagnostics:
aws:s3:Bucket (test):
error: The role "arn:aws:iam::XXXXXXXXX:role/testrole-6671910" cannot be assumed.
There are a number of possible causes of this - the most common are:
* The credentials used in order to assume the role are invalid
* The credentials do not have appropriate permission to assume the role
* The role ARN is not valid
What is the error message that you get here?
Note that you could put a delay inside an apply (setTimeout inside the apply) as a workaround
Sounds like the root issue is that the provider needs to retry the assume role lookup to deal with eventual consistency of IAM. That likely require an upstream AWS provider fix.
@lukehoban I added the error output above.
~Do you have an example for using setTimeout inside apply?~
I found this example, will try the setTimeout method now.
UPDATE: This seems to work, could be further improved by checking if the role can be assumed inside the .apply instead of using setTimeout
const roleProvider = new aws.Provider(
"test",
{
assumeRole: {
roleArn: role.arn.apply(async(arn) => {
if (!pulumi.runtime.isDryRun()) {
await new Promise(resolve => setTimeout(resolve, 30 * 1000));
}
return arn
})
}
},
{ dependsOn: rolePolicy}
);
@mazamats Thanks for updating the issue with a clear example. I was stuck on the same issue and your example really helped.
For the Python users out there, here's a similar example of how to do the above, but with sts.assume_role() from boto3:
import asyncio
import boto3
import botocore.exceptions
import pulumi_aws as aws
import pulumi
async def wait_for_iam_eventual_consistency(args: list) -> str:
role_arn = args[0]
role_session_name = args[1]
duration = 3
attempts = 10
if not pulumi.runtime.is_dry_run():
pulumi.log.info(f"Waiting up to {attempts * duration} seconds for IAM eventual consistency for IAM Role: {role_arn}")
sts = boto3.client('sts')
for i in range(attempts):
try:
sts.assume_role(
RoleArn=role_arn,
RoleSessionName=role_session_name
)
except botocore.exceptions.ClientError:
await asyncio.sleep(duration)
else:
break
return role_arn
account_id = aws.get_caller_identity().account_id
iam_role = aws.iam.Role(
resource_name="vpc-admin",
description='IAM Role for VPC Administration',
path='/',
force_detach_policies=True,
assume_role_policy={
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowAccountAssumeRole",
"Action": "sts:AssumeRole",
"Principal": {
"AWS": f"arn:aws:iam::{account_id}:root"
},
"Effect": "Allow"
}]
}
)
iam_role_policy_attachment = aws.iam.RolePolicyAttachment(
resource_name="vpc-admin-policy-attachment",
role=iam_role.id,
policy_arn="arn:aws:iam::aws:policy/AmazonVPCFullAccess"
)
session_name = "VpcAdmin"
role_arn = pulumi.Output.all(iam_role.arn, session_name).apply(wait_for_iam_eventual_consistency)
iam_role_provider = aws.Provider(
resource_name='vpc-admin-provider',
assume_role={
"role_arn": role_arn,
"session_name": session_name
},
opts=pulumi.ResourceOptions(
depends_on=[
iam_role_policy_attachment
]
)
)
aws.ec2.Vpc(
resource_name="test-vpc",
cidr_block="10.100.0.0/16",
enable_dns_hostnames=True,
enable_dns_support=True,
tags={
"Name": "test-vpc",
"CreatedBy": "vpc-admin-provider"
},
opts=pulumi.ResourceOptions(
parent=iam_role_provider,
provider=iam_role_provider
)
)
This appears to be due to upstream https://github.com/hashicorp/terraform-provider-aws/issues/6566.