pulumi-aws
pulumi-aws copied to clipboard
Pulumi does not support role-based access with MFA devices in AWS profiles
Expected behavior
It would be preferred if the AWS Provider understood the AWS config the same way as the aws cli.
Current behavior
Given an ~/.aws/config
with sections similar to below:
[profile grey-sandbox-user]
region = us-east-1
output = json
[profile grey-sandbox-deployment]
mfa_serial = arn:aws:iam::0123456789:mfa/catmeme
role_arn = arn:aws:iam::0123456789:role/deployment-access
source_profile = grey-sandbox-user
region = us-east-1
AWS cli behavior:
aws --profile grey-sandbox-deployment ec2 describe-instances
Enter MFA code for arn:aws:iam::0123456789:mfa/catmeme:
Subsequent requests will use ~/.aws/cli/cache/<some_id>.json
.
However, aws:profile
in Pulumi.sandbox.yaml
will not work, generating an error:
pulumi preview
...
Error: invocation of aws:index/getCallerIdentity:getCallerIdentity returned an error: unable to discover AWS AccessKeyID and/or SecretAccessKey - see https://pulumi.io/install/aws.html for details on configuration
Furthermore, when using the work-around below, if the token (AWS_SESSION_TOKEN
) expires Pulumi will hang.
Steps to reproduce
See above.
Context (Environment)
We need to implement role-based access with MFA, but this is blocking us for doing it purely with Pulumi.
Workaround
Generate environment variables, AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
, and AWS_SESSION_TOKEN
.
Here is a bash/zsh function that wraps Pulumi to support the expected behavior:
function pulumi () {
local PULUMI_COMMANDS_AWS_REQUIRED=(destroy logs preview refresh up update watch)
local AWS_REQUIRED=$([[ " ${PULUMI_COMMANDS_AWS_REQUIRED[@]} " =~ " ${1} " ]] && echo "true")
if [[ -n ${AWS_REQUIRED} ]]; then
local PULUMI_AWS_PROFILE=$(command pulumi config get aws:profile 2> /dev/null)
local ROLE_ARN=$(aws configure get profile.${PULUMI_AWS_PROFILE}.role_arn)
fi
if [[ -n ${ROLE_ARN} ]]; then
echo "Using AWS Profile: ${PULUMI_AWS_PROFILE}"
local AWS_ROLE_USER_ID=$(aws --profile ${PULUMI_AWS_PROFILE} sts get-caller-identity |jq -r '.UserId')
if [[ -z ${AWS_ROLE_USER_ID} ]]; then return; fi
local AWS_CREDENTIALS=$(grep -hs ${AWS_ROLE_USER_ID} ~/.aws/cli/cache/*.json)
AWS_ACCESS_KEY_ID=$(echo ${AWS_CREDENTIALS} | jq -r '.Credentials.AccessKeyId') \
AWS_SECRET_ACCESS_KEY=$(echo ${AWS_CREDENTIALS} | jq -r '.Credentials.SecretAccessKey') \
AWS_SESSION_TOKEN=$(echo ${AWS_CREDENTIALS} | jq -r '.Credentials.SessionToken') \
command pulumi ${@}
else
command pulumi ${@}
fi
}
Related issues:
- https://github.com/pulumi/pulumi-aws/issues/584
- https://github.com/pulumi/pulumi-aws/issues/252#issuecomment-584903094
The above works just fine with 4.x but with the new MAJOR, 5.x I receive:
error configuring Terraform AWS Provider: loading configuration: assume role with MFA enabled, but AssumeRoleTokenProvider session option not set
I came up with a temporary work-around for the new MAJOR.
You can explicitly declare an aws provider and pass it in the opts to each one of your resource declarations.
const awsProvider = new aws.Provider("aws-provider", {
accessKey: process.env.AWS_ACCESS_KEY_ID,
region: "us-east-1",
secretKey: process.env.AWS_SECRET_ACCESS_KEY,
token: process.env.AWS_SESSION_TOKEN,
});
Unfortunately, I was unable to get it this working with the default provider. I attempted the below:
aws.sdk.config.credentials = {
accessKeyId: process.env.AWS_ACCESS_KEY_ID as string,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string,
sessionToken: process.env.AWS_SESSION_TOKEN as string,
};
A separate work-around is to only provide the aws:region
in stack configuration YAMLs, track the profile name as a separate project variable, and in the wrapper pass that profile as AWS_PROFILE
before the pulumi
command.
Still would like to see a better solution from Pulumi.