diff --git a/builtin/providers/aws/auth_helpers.go b/builtin/providers/aws/auth_helpers.go index fab4928b6dbf..cb2dec0df6a8 100644 --- a/builtin/providers/aws/auth_helpers.go +++ b/builtin/providers/aws/auth_helpers.go @@ -146,8 +146,8 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) { // Otherwise we need to construct and STS client with the main credentials, and verify // that we can assume the defined role. - log.Printf("[INFO] Attempting to AssumeRole %s (SessionName: %q, ExternalId: %q)", - c.AssumeRoleARN, c.AssumeRoleSessionName, c.AssumeRoleExternalID) + log.Printf("[INFO] Attempting to AssumeRole %s (SessionName: %q, ExternalId: %q, MFA Serial: %q)", + c.AssumeRoleARN, c.AssumeRoleSessionName, c.AssumeRoleExternalID, c.AssumeRoleMFASerial) creds := awsCredentials.NewChainCredentials(providers) cp, err := creds.Get() @@ -182,11 +182,16 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) { if c.AssumeRoleExternalID != "" { assumeRoleProvider.ExternalID = aws.String(c.AssumeRoleExternalID) } + if c.AssumeRoleMFASerial != "" { + assumeRoleProvider.SerialNumber = aws.String(c.AssumeRoleMFASerial) + assumeRoleProvider.TokenCode = aws.String(c.AssumeRoleTokenCode) + } providers = []awsCredentials.Provider{assumeRoleProvider} assumeRoleCreds := awsCredentials.NewChainCredentials(providers) _, err = assumeRoleCreds.Get() + if err != nil { if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" { return nil, fmt.Errorf("The role %q cannot be assumed.\n\n"+ diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index 101a450a85a2..f823a2014f51 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -75,6 +75,8 @@ type Config struct { AssumeRoleARN string AssumeRoleExternalID string AssumeRoleSessionName string + AssumeRoleMFASerial string + AssumeRoleTokenCode string AllowedAccountIds []interface{} ForbiddenAccountIds []interface{} diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index a9ecf320fde9..8d10363bae81 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -415,6 +415,9 @@ func init() { "assume_role_external_id": "The external ID to use when assuming the role. If omitted," + " no external ID is passed to the AssumeRole call.", + + "assume_role_mfa_serial": "The serial of a MFA device.", + "assume_role_token_code": "The MFA OTP code.", } } @@ -442,6 +445,8 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { config.AssumeRoleARN = assumeRole["role_arn"].(string) config.AssumeRoleSessionName = assumeRole["session_name"].(string) config.AssumeRoleExternalID = assumeRole["external_id"].(string) + config.AssumeRoleMFASerial = assumeRole["mfa_serial"].(string) + config.AssumeRoleTokenCode = assumeRole["token_code"].(string) log.Printf("[INFO] assume_role configuration set: (ARN: %q, SessionID: %q, ExternalID: %q)", config.AssumeRoleARN, config.AssumeRoleSessionName, config.AssumeRoleExternalID) } else { @@ -496,6 +501,17 @@ func assumeRoleSchema() *schema.Schema { Optional: true, Description: descriptions["assume_role_external_id"], }, + + "mfa_serial": { + Type: schema.TypeString, + Optional: true, + Description: descriptions["assume_role_mfa_serial"], + }, + "token_code": { + Type: schema.TypeString, + Optional: true, + Description: descriptions["assume_role_token_code"], + }, }, }, Set: assumeRoleToHash, @@ -508,6 +524,8 @@ func assumeRoleToHash(v interface{}) int { buf.WriteString(fmt.Sprintf("%s-", m["role_arn"].(string))) buf.WriteString(fmt.Sprintf("%s-", m["session_name"].(string))) buf.WriteString(fmt.Sprintf("%s-", m["external_id"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["mfa_serial"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["token_code"].(string))) return hashcode.String(buf.String()) } diff --git a/website/source/docs/providers/aws/index.html.markdown b/website/source/docs/providers/aws/index.html.markdown index a86bae4ffba8..c64ca13473f4 100644 --- a/website/source/docs/providers/aws/index.html.markdown +++ b/website/source/docs/providers/aws/index.html.markdown @@ -30,7 +30,7 @@ resource "aws_instance" "web" { } ``` -## Authentication +## Authentication The AWS provider offers flexible means of providing credentials for authentication. The following methods are supported, in this order, and @@ -46,7 +46,7 @@ explained below: Static credentials can be provided by adding an `access_key` and `secret_key` in-line in the aws provider block: -Usage: +Usage: ``` provider "aws" { @@ -58,7 +58,7 @@ provider "aws" { ###Environment variables -You can provide your credentials via `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, +You can provide your credentials via `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, environment variables, representing your AWS Access Key and AWS Secret Key, respectively. `AWS_DEFAULT_REGION` and `AWS_SESSION_TOKEN` are also used, if applicable: @@ -69,7 +69,7 @@ provider "aws" {} Usage: ``` -$ export AWS_ACCESS_KEY_ID="anaccesskey" +$ export AWS_ACCESS_KEY_ID="anaccesskey" $ export AWS_SECRET_ACCESS_KEY="asecretkey" $ export AWS_DEFAULT_REGION="us-west-2" $ terraform plan @@ -78,7 +78,7 @@ $ terraform plan ###Shared Credentials file You can use an AWS credentials file to specify your credentials. The default -location is `$HOME/.aws/credentials` on Linux and OSX, or `"%USERPROFILE%\.aws\credentials"` +location is `$HOME/.aws/credentials` on Linux and OSX, or `"%USERPROFILE%\.aws\credentials"` for Windows users. If we fail to detect credentials inline, or in the environment, Terraform will check this location. You can optionally specify a different location in the configuration by providing `shared_credentials_file`, @@ -86,7 +86,7 @@ or in the environment with the `AWS_SHARED_CREDENTIALS_FILE` variable. This method also supports a `profile` configuration and matching `AWS_PROFILE` environment variable: -Usage: +Usage: ``` provider "aws" { @@ -124,6 +124,8 @@ provider "aws" { role_arn = "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME" session_name = "SESSION_NAME" external_id = "EXTERNAL_ID" + mfa_serial = "arn:aws:iam::ACCOUNT_ID:mfa/USERNAME" + token_code = "${var.token_code}" } } ``` @@ -215,7 +217,13 @@ The nested `assume_role` block supports the following: AssumeRole call. * `external_id` - (Optional) The external ID to use when making the - AssumeRole call. + AssumeRole call. + +* `mfa_serial` - (Optional) The MFA serial to use when making the + AssumeRole call. + +* `token_code` - (Optional) The MFA OTP code to use when making the + AssumeRole call. Required if `mfa_serial` is given. Nested `endpoints` block supports the following: