terraform-provider-aws: AssumeRoleTokenProviderNotSetError when using assume_role with mfa enabled

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave “+1” or “me too” comments, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Terraform Version

Terraform version: 0.12.10 Go runtime version: go1.13.1 provider.aws ~> 2.32.0

Affected Resource(s)

  • provider “aws”

Terraform Configuration Files

env

AWS_SDK_LOAD_CONFIG=1
AWS_PROFILE=bar-admin

~/.aws/config

[default]
region = eu-west-1
output = json

[profile bar-default]
aws_access_key_id=<key>
aws_secret_access_key=<secret>

[profile bar-admin]
role_arn=arn:aws:iam::<account_id>:role/admin
source_profile=bar-default
mfa_serial=arn:aws:iam::<account_id>:mfa/ch
resource "aws_iam_group" "admins" {
  name = "admins"
}

resource "aws_iam_role" "admin_mfa_role" {
  name = "admin"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]
}
EOF
}

resource "aws_iam_group_policy_attachment" "admins_assume_role" {
  group      = "${aws_iam_group.admins.name}"
  policy_arn = "${aws_iam_policy.assume_role.arn}"
}

resource "aws_iam_policy" "assume_role" {
  name = "assume_role"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::*:role/*"
  }
}
EOF
}

resource "aws_iam_user" "ch" {
  name = "ch"
}

resource "aws_iam_user_login_profile" "ch_login" {
  user    = "${aws_iam_user.ch.name}"
  pgp_key = "keybase:chrishowell"
}

resource "aws_iam_user_group_membership" "ch_groups" {
  user = "${aws_iam_user.ch.name}"

  groups = [
    "${aws_iam_group.admins.name}"
  ]
}

Debug Output

https://gist.github.com/chrishowell/ddd169c24ba4f0fcaba70a3e2f624a5a

Panic Output

N/A

Expected Behavior

As of terraform-provider-aws_v2.32.0 I believe assume_role with mfa enabled should work.

Actual Behavior

Error: error creating EC2 Metadata session: AssumeRoleTokenProviderNotSetError: assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.

Steps to Reproduce

  1. terraform plan

Important Factoids

Running locally on Mac OSX Catalina Brew install of Terraform No ~/.aws/configuration file present

References

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 123
  • Comments: 17 (3 by maintainers)

Most upvoted comments

Any update on this issue? I am having the exactly same problem. I think that using assume_role with MFA is probably the most common way of access control in big organizations. In my opinion, specifying profile should be enough for Terraform to recognize the use of assume_role with MFA since the configuration is already in ~/.aws/config and ~/.aws/credentials.

One problem is this role doesn’t seem to work with aws-vault now. To fix that you will likely want to make an assumed_role profile and an assumed_role_tf profile to support both.

To add (for aws-vault users) there’s also include_profile so you can do this:

[profile base]
credential_process = sh -c 'aws-vault exec base --json 2> $(tty)'
mfa_serial = arn:aws:iam::<account a>:mfa/<user_name>
region = us-east-1

[profile assumed_role]
role_arn = arn:aws:iam::<account b>:role/<role_to_assume>
source_profile = base
region = us-east-1
# For aws-vault only:
include_profile = base

This way aws-vault exec will know about the mfa_serial and prompt for MFA accordingly, and Terraform won’t see the mfa_serial in assumed_role and lets aws-vault do the work.

As a workaround for anyone running into this when using aws-vault (maybe others?) it seems you can remove mfa_serial from the role profile while having it still in the base profile and it will work.

So for example:

[profile base]
credential_process = sh -c 'aws-vault exec base --json 2> $(tty)'
mfa_serial = arn:aws:iam::<redacted>:mfa/<user_name>
region = us-east-1

[profile assumed_role]
role_arn = arn:aws:iam::<redacted>:role/<role_to_assume>
source_profile = base
region = us-east-1

Note that the assumed_role does not have a mfa_serial option set.

And then for your provider something like this should work:

provider "aws" {
  region  = "us-east-1
  profile = "assumed_role"
}

One problem is this role doesn’t seem to work with aws-vault now. To fix that you will likely want to make an assumed_role profile and an assumed_role_tf profile to support both.

It almost seems like in this specific case if an error was not raised, this would simply work.

Note, I finally read https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/version-4-upgrade#changes-to-authentication and it gave me a pretty big clue. Removing the profile attribute from the provider "aws" {} block allows me to run terraform in an assumed AWS role using aws-vault.

I use the following bash function to get around this issue:

function set_aws_creds () {
    unset AWS_ACCESS_KEY_ID
    unset AWS_SECRET_ACCESS_KEY
    unset AWS_SESSION_TOKEN

    ROLE="$( aws.exe configure get role_arn --profile $1 )"
    read AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN <<< $(aws.exe sts assume-role --role-arn $ROLE --role-session-name set-aws-creds-session --output text | awk '/^CREDENTIALS/ {print $2, $4, $5 }')
    export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    export AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN
}

This is based on this StackOverflow answer: https://stackoverflow.com/a/53199639.

I ran into a similar issue with the terraform language server, and I change the credential helper process line to something like this which will trigger a dialog box for the MFA prompt:

credential_process=aws-vault exec base --json --prompt=osascript

I do wish this configuration is more flexible because I do prefer TTY input in most other cases.

This workaround works well but isn’t flawless. When using VS Code with the Terraform plugin containing Terraform Language Server for example, the language server will perform Terraform commands in the background. While it doesn’t output any error message, it tries to call aws-vault which in turn ends up writing a file not a tty in the project directory, since the language server doesn’t provide a tty. I have yet to find a solution to that other than adding not a tty to .gitignore which seems like a pretty crappy solution.

I’m on Ubuntu and I believe osascript is macOS specific. So, it doesn’t sound like a solution I could use.

Anyway, this works for me but I’m not proud of it:

credential_process=sh -c "if [ $(echo $(tty) | cut -c 1-5) = '/dev/' ]; then aws-vault exec base --json 2> $(tty); else return 0; fi"

Works as before, minus not a tty files created by Terraform Language Server.

Hello, everyone. The issue #2420 also addresses the lack of support for MFA tokens in the AWS Provider. So that any future discussion will be in one place, I’m going to close this issue.

See https://github.com/hashicorp/terraform-provider-aws/issues/2420#issuecomment-1449084088 for the current status of the issue

One problem is this role doesn’t seem to work with aws-vault now. To fix that you will likely want to make an assumed_role profile and an assumed_role_tf profile to support both.

To add (for aws-vault users) there’s also include_profile so you can do this:

[profile base]
credential_process = sh -c 'aws-vault exec base --json 2> $(tty)'
mfa_serial = arn:aws:iam::<account a>:mfa/<user_name>
region = us-east-1

[profile assumed_role]
role_arn = arn:aws:iam::<account b>:role/<role_to_assume>
source_profile = base
region = us-east-1
# For aws-vault only:
include_profile = base

This way aws-vault exec will know about the mfa_serial and prompt for MFA accordingly, and Terraform won’t see the mfa_serial in assumed_role and lets aws-vault do the work.

This workaround works well but isn’t flawless. When using VS Code with the Terraform plugin containing Terraform Language Server for example, the language server will perform Terraform commands in the background. While it doesn’t output any error message, it tries to call aws-vault which in turn ends up writing a file not a tty in the project directory, since the language server doesn’t provide a tty. I have yet to find a solution to that other than adding not a tty to .gitignore which seems like a pretty crappy solution.