pulumi-aws icon indicating copy to clipboard operation
pulumi-aws copied to clipboard

Pulumi does not support role-based access with MFA devices in AWS profiles

Open catmeme opened this issue 4 years ago • 3 comments

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

catmeme avatar Feb 24 '21 20:02 catmeme

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

catmeme avatar May 09 '22 15:05 catmeme

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,
};

catmeme avatar May 09 '22 16:05 catmeme

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.

catmeme avatar Jul 20 '22 13:07 catmeme