pulumi / pulumi-aws

An Amazon Web Services (AWS) Pulumi resource package, providing multi-language access to AWS
Apache License 2.0
446 stars 155 forks source link

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

Open catmeme opened 3 years ago

catmeme commented 3 years ago

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:

catmeme commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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.

vladimir-rezan commented 1 year ago

Pretty important for us since the pulumi role would be highly privileged. It's worth noting that kubectl is able to support the awscli behaviour, passing the prompt to the end user.

superical commented 1 year ago

Are there any updates on this issue? This looks like a pretty important feature to have.