mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Merge pull request #8638 from hashicorp/f-aws-assume-role
provider/aws: Add support for AssumeRole prior to operations
This commit is contained in:
commit
94ca84e772
@ -1,6 +1,7 @@
|
|||||||
package aws
|
package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@ -11,6 +12,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
|
awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
|
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
@ -75,7 +77,7 @@ func GetAccountId(iamconn *iam.IAM, stsconn *sts.STS, authProviderName string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(outRoles.Roles) < 1 {
|
if len(outRoles.Roles) < 1 {
|
||||||
return "", fmt.Errorf("Failed getting account ID via 'iam:ListRoles': No roles available")
|
return "", errors.New("Failed getting account ID via 'iam:ListRoles': No roles available")
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseAccountIdFromArn(*outRoles.Roles[0].Arn)
|
return parseAccountIdFromArn(*outRoles.Roles[0].Arn)
|
||||||
@ -92,7 +94,7 @@ func parseAccountIdFromArn(arn string) (string, error) {
|
|||||||
// This function is responsible for reading credentials from the
|
// This function is responsible for reading credentials from the
|
||||||
// environment in the case that they're not explicitly specified
|
// environment in the case that they're not explicitly specified
|
||||||
// in the Terraform configuration.
|
// in the Terraform configuration.
|
||||||
func GetCredentials(c *Config) *awsCredentials.Credentials {
|
func GetCredentials(c *Config) (*awsCredentials.Credentials, error) {
|
||||||
// build a chain provider, lazy-evaulated by aws-sdk
|
// build a chain provider, lazy-evaulated by aws-sdk
|
||||||
providers := []awsCredentials.Provider{
|
providers := []awsCredentials.Provider{
|
||||||
&awsCredentials.StaticProvider{Value: awsCredentials.Value{
|
&awsCredentials.StaticProvider{Value: awsCredentials.Value{
|
||||||
@ -126,7 +128,7 @@ func GetCredentials(c *Config) *awsCredentials.Credentials {
|
|||||||
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
|
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
|
||||||
Client: metadataClient,
|
Client: metadataClient,
|
||||||
})
|
})
|
||||||
log.Printf("[INFO] AWS EC2 instance detected via default metadata" +
|
log.Print("[INFO] AWS EC2 instance detected via default metadata" +
|
||||||
" API endpoint, EC2RoleProvider added to the auth chain")
|
" API endpoint, EC2RoleProvider added to the auth chain")
|
||||||
} else {
|
} else {
|
||||||
if usedEndpoint == "" {
|
if usedEndpoint == "" {
|
||||||
@ -137,7 +139,68 @@ func GetCredentials(c *Config) *awsCredentials.Credentials {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return awsCredentials.NewChainCredentials(providers)
|
// This is the "normal" flow (i.e. not assuming a role)
|
||||||
|
if c.AssumeRoleARN == "" {
|
||||||
|
return awsCredentials.NewChainCredentials(providers), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
creds := awsCredentials.NewChainCredentials(providers)
|
||||||
|
cp, err := creds.Get()
|
||||||
|
if err != nil {
|
||||||
|
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
|
||||||
|
return nil, errors.New(`No valid credential sources found for AWS Provider.
|
||||||
|
Please see https://terraform.io/docs/providers/aws/index.html for more information on
|
||||||
|
providing credentials for the AWS Provider`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
|
||||||
|
|
||||||
|
awsConfig := &aws.Config{
|
||||||
|
Credentials: creds,
|
||||||
|
Region: aws.String(c.Region),
|
||||||
|
MaxRetries: aws.Int(c.MaxRetries),
|
||||||
|
HTTPClient: cleanhttp.DefaultClient(),
|
||||||
|
S3ForcePathStyle: aws.Bool(c.S3ForcePathStyle),
|
||||||
|
}
|
||||||
|
|
||||||
|
stsclient := sts.New(session.New(awsConfig))
|
||||||
|
assumeRoleProvider := &stscreds.AssumeRoleProvider{
|
||||||
|
Client: stsclient,
|
||||||
|
RoleARN: c.AssumeRoleARN,
|
||||||
|
}
|
||||||
|
if c.AssumeRoleSessionName != "" {
|
||||||
|
assumeRoleProvider.RoleSessionName = c.AssumeRoleSessionName
|
||||||
|
}
|
||||||
|
if c.AssumeRoleExternalID != "" {
|
||||||
|
assumeRoleProvider.ExternalID = aws.String(c.AssumeRoleExternalID)
|
||||||
|
}
|
||||||
|
|
||||||
|
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"+
|
||||||
|
" There are a number of possible causes of this - the most common are:\n"+
|
||||||
|
" * The credentials used in order to assume the role are invalid\n"+
|
||||||
|
" * The credentials do not have appropriate permission to assume the role\n"+
|
||||||
|
" * The role ARN is not valid",
|
||||||
|
c.AssumeRoleARN)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return assumeRoleCreds, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setOptionalEndpoint(cfg *aws.Config) string {
|
func setOptionalEndpoint(cfg *aws.Config) string {
|
||||||
|
@ -51,7 +51,7 @@ func TestAWSGetAccountId_shouldBeValid_EC2RoleHasPriority(t *testing.T) {
|
|||||||
defer awsTs()
|
defer awsTs()
|
||||||
|
|
||||||
iamEndpoints := []*iamEndpoint{
|
iamEndpoints := []*iamEndpoint{
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
||||||
Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"},
|
Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"},
|
||||||
},
|
},
|
||||||
@ -72,7 +72,7 @@ func TestAWSGetAccountId_shouldBeValid_EC2RoleHasPriority(t *testing.T) {
|
|||||||
|
|
||||||
func TestAWSGetAccountId_shouldBeValid_fromIamUser(t *testing.T) {
|
func TestAWSGetAccountId_shouldBeValid_fromIamUser(t *testing.T) {
|
||||||
iamEndpoints := []*iamEndpoint{
|
iamEndpoints := []*iamEndpoint{
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
||||||
Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"},
|
Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"},
|
||||||
},
|
},
|
||||||
@ -94,11 +94,11 @@ func TestAWSGetAccountId_shouldBeValid_fromIamUser(t *testing.T) {
|
|||||||
|
|
||||||
func TestAWSGetAccountId_shouldBeValid_fromGetCallerIdentity(t *testing.T) {
|
func TestAWSGetAccountId_shouldBeValid_fromGetCallerIdentity(t *testing.T) {
|
||||||
iamEndpoints := []*iamEndpoint{
|
iamEndpoints := []*iamEndpoint{
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
||||||
Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
|
Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
|
||||||
},
|
},
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"},
|
Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"},
|
||||||
Response: &iamResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"},
|
Response: &iamResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"},
|
||||||
},
|
},
|
||||||
@ -119,15 +119,15 @@ func TestAWSGetAccountId_shouldBeValid_fromGetCallerIdentity(t *testing.T) {
|
|||||||
|
|
||||||
func TestAWSGetAccountId_shouldBeValid_fromIamListRoles(t *testing.T) {
|
func TestAWSGetAccountId_shouldBeValid_fromIamListRoles(t *testing.T) {
|
||||||
iamEndpoints := []*iamEndpoint{
|
iamEndpoints := []*iamEndpoint{
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
||||||
Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
|
Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
|
||||||
},
|
},
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"},
|
Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"},
|
||||||
Response: &iamResponse{403, stsResponse_GetCallerIdentity_unauthorized, "text/xml"},
|
Response: &iamResponse{403, stsResponse_GetCallerIdentity_unauthorized, "text/xml"},
|
||||||
},
|
},
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
|
Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
|
||||||
Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"},
|
Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"},
|
||||||
},
|
},
|
||||||
@ -148,11 +148,11 @@ func TestAWSGetAccountId_shouldBeValid_fromIamListRoles(t *testing.T) {
|
|||||||
|
|
||||||
func TestAWSGetAccountId_shouldBeValid_federatedRole(t *testing.T) {
|
func TestAWSGetAccountId_shouldBeValid_federatedRole(t *testing.T) {
|
||||||
iamEndpoints := []*iamEndpoint{
|
iamEndpoints := []*iamEndpoint{
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
||||||
Response: &iamResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"},
|
Response: &iamResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"},
|
||||||
},
|
},
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
|
Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
|
||||||
Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"},
|
Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"},
|
||||||
},
|
},
|
||||||
@ -173,11 +173,11 @@ func TestAWSGetAccountId_shouldBeValid_federatedRole(t *testing.T) {
|
|||||||
|
|
||||||
func TestAWSGetAccountId_shouldError_unauthorizedFromIam(t *testing.T) {
|
func TestAWSGetAccountId_shouldError_unauthorizedFromIam(t *testing.T) {
|
||||||
iamEndpoints := []*iamEndpoint{
|
iamEndpoints := []*iamEndpoint{
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
|
||||||
Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
|
Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
|
||||||
},
|
},
|
||||||
&iamEndpoint{
|
{
|
||||||
Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
|
Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
|
||||||
Response: &iamResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"},
|
Response: &iamResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"},
|
||||||
},
|
},
|
||||||
@ -218,15 +218,20 @@ func TestAWSGetCredentials_shouldError(t *testing.T) {
|
|||||||
defer resetEnv()
|
defer resetEnv()
|
||||||
cfg := Config{}
|
cfg := Config{}
|
||||||
|
|
||||||
c := GetCredentials(&cfg)
|
c, err := GetCredentials(&cfg)
|
||||||
_, err := c.Get()
|
|
||||||
if awsErr, ok := err.(awserr.Error); ok {
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
if awsErr.Code() != "NoCredentialProviders" {
|
if awsErr.Code() != "NoCredentialProviders" {
|
||||||
t.Fatalf("Expected NoCredentialProviders error")
|
t.Fatal("Expected NoCredentialProviders error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = c.Get()
|
||||||
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
if awsErr.Code() != "NoCredentialProviders" {
|
||||||
|
t.Fatal("Expected NoCredentialProviders error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected an error with empty env, keys, and IAM in AWS Config")
|
t.Fatal("Expected an error with empty env, keys, and IAM in AWS Config")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,14 +256,19 @@ func TestAWSGetCredentials_shouldBeStatic(t *testing.T) {
|
|||||||
Token: c.Token,
|
Token: c.Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
creds := GetCredentials(&cfg)
|
creds, err := GetCredentials(&cfg)
|
||||||
if creds == nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected a static creds provider to be returned")
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
}
|
}
|
||||||
|
if creds == nil {
|
||||||
|
t.Fatal("Expected a static creds provider to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error gettings creds: %s", err)
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.AccessKeyID != c.Key {
|
if v.AccessKeyID != c.Key {
|
||||||
t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID)
|
t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID)
|
||||||
}
|
}
|
||||||
@ -286,9 +296,12 @@ func TestAWSGetCredentials_shouldIAM(t *testing.T) {
|
|||||||
// An empty config, no key supplied
|
// An empty config, no key supplied
|
||||||
cfg := Config{}
|
cfg := Config{}
|
||||||
|
|
||||||
creds := GetCredentials(&cfg)
|
creds, err := GetCredentials(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
|
}
|
||||||
if creds == nil {
|
if creds == nil {
|
||||||
t.Fatalf("Expected a static creds provider to be returned")
|
t.Fatal("Expected a static creds provider to be returned")
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
@ -335,10 +348,14 @@ func TestAWSGetCredentials_shouldIgnoreIAM(t *testing.T) {
|
|||||||
Token: c.Token,
|
Token: c.Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
creds := GetCredentials(&cfg)
|
creds, err := GetCredentials(&cfg)
|
||||||
if creds == nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected a static creds provider to be returned")
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
}
|
}
|
||||||
|
if creds == nil {
|
||||||
|
t.Fatal("Expected a static creds provider to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error gettings creds: %s", err)
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
@ -362,7 +379,14 @@ func TestAWSGetCredentials_shouldErrorWithInvalidEndpoint(t *testing.T) {
|
|||||||
ts := invalidAwsEnv(t)
|
ts := invalidAwsEnv(t)
|
||||||
defer ts()
|
defer ts()
|
||||||
|
|
||||||
creds := GetCredentials(&Config{})
|
creds, err := GetCredentials(&Config{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
|
}
|
||||||
|
if creds == nil {
|
||||||
|
t.Fatal("Expected a static creds provider to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error returned when getting creds w/ invalid EC2 endpoint")
|
t.Fatal("Expected error returned when getting creds w/ invalid EC2 endpoint")
|
||||||
@ -380,11 +404,17 @@ func TestAWSGetCredentials_shouldIgnoreInvalidEndpoint(t *testing.T) {
|
|||||||
ts := invalidAwsEnv(t)
|
ts := invalidAwsEnv(t)
|
||||||
defer ts()
|
defer ts()
|
||||||
|
|
||||||
creds := GetCredentials(&Config{AccessKey: "accessKey", SecretKey: "secretKey"})
|
creds, err := GetCredentials(&Config{AccessKey: "accessKey", SecretKey: "secretKey"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
|
}
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Getting static credentials w/ invalid EC2 endpoint failed: %s", err)
|
t.Fatalf("Getting static credentials w/ invalid EC2 endpoint failed: %s", err)
|
||||||
}
|
}
|
||||||
|
if creds == nil {
|
||||||
|
t.Fatal("Expected a static creds provider to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
if v.ProviderName != "StaticProvider" {
|
if v.ProviderName != "StaticProvider" {
|
||||||
t.Fatalf("Expected provider name to be %q, %q given", "StaticProvider", v.ProviderName)
|
t.Fatalf("Expected provider name to be %q, %q given", "StaticProvider", v.ProviderName)
|
||||||
@ -406,10 +436,14 @@ func TestAWSGetCredentials_shouldCatchEC2RoleProvider(t *testing.T) {
|
|||||||
ts := awsEnv(t)
|
ts := awsEnv(t)
|
||||||
defer ts()
|
defer ts()
|
||||||
|
|
||||||
creds := GetCredentials(&Config{})
|
creds, err := GetCredentials(&Config{})
|
||||||
if creds == nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected an EC2Role creds provider to be returned")
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
}
|
}
|
||||||
|
if creds == nil {
|
||||||
|
t.Fatal("Expected an EC2Role creds provider to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error when getting creds: %s", err)
|
t.Fatalf("Expected no error when getting creds: %s", err)
|
||||||
@ -452,10 +486,14 @@ func TestAWSGetCredentials_shouldBeShared(t *testing.T) {
|
|||||||
t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
|
t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
creds := GetCredentials(&Config{Profile: "myprofile", CredsFilename: file.Name()})
|
creds, err := GetCredentials(&Config{Profile: "myprofile", CredsFilename: file.Name()})
|
||||||
if creds == nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected a provider chain to be returned")
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
}
|
}
|
||||||
|
if creds == nil {
|
||||||
|
t.Fatal("Expected a provider chain to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error gettings creds: %s", err)
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
@ -479,10 +517,14 @@ func TestAWSGetCredentials_shouldBeENV(t *testing.T) {
|
|||||||
defer resetEnv()
|
defer resetEnv()
|
||||||
|
|
||||||
cfg := Config{}
|
cfg := Config{}
|
||||||
creds := GetCredentials(&cfg)
|
creds, err := GetCredentials(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
|
}
|
||||||
if creds == nil {
|
if creds == nil {
|
||||||
t.Fatalf("Expected a static creds provider to be returned")
|
t.Fatalf("Expected a static creds provider to be returned")
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error gettings creds: %s", err)
|
t.Fatalf("Error gettings creds: %s", err)
|
||||||
|
@ -2,6 +2,7 @@ package aws
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -54,7 +55,6 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/sts"
|
"github.com/aws/aws-sdk-go/service/sts"
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/go-cleanhttp"
|
"github.com/hashicorp/go-cleanhttp"
|
||||||
"github.com/hashicorp/go-multierror"
|
|
||||||
"github.com/hashicorp/terraform/helper/logging"
|
"github.com/hashicorp/terraform/helper/logging"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
@ -68,6 +68,10 @@ type Config struct {
|
|||||||
Region string
|
Region string
|
||||||
MaxRetries int
|
MaxRetries int
|
||||||
|
|
||||||
|
AssumeRoleARN string
|
||||||
|
AssumeRoleExternalID string
|
||||||
|
AssumeRoleSessionName string
|
||||||
|
|
||||||
AllowedAccountIds []interface{}
|
AllowedAccountIds []interface{}
|
||||||
ForbiddenAccountIds []interface{}
|
ForbiddenAccountIds []interface{}
|
||||||
|
|
||||||
@ -135,34 +139,33 @@ type AWSClient struct {
|
|||||||
func (c *Config) Client() (interface{}, error) {
|
func (c *Config) Client() (interface{}, error) {
|
||||||
// Get the auth and region. This can fail if keys/regions were not
|
// Get the auth and region. This can fail if keys/regions were not
|
||||||
// specified and we're attempting to use the environment.
|
// specified and we're attempting to use the environment.
|
||||||
var errs []error
|
|
||||||
|
|
||||||
log.Println("[INFO] Building AWS region structure")
|
log.Println("[INFO] Building AWS region structure")
|
||||||
err := c.ValidateRegion()
|
err := c.ValidateRegion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var client AWSClient
|
var client AWSClient
|
||||||
if len(errs) == 0 {
|
|
||||||
// store AWS region in client struct, for region specific operations such as
|
// store AWS region in client struct, for region specific operations such as
|
||||||
// bucket storage in S3
|
// bucket storage in S3
|
||||||
client.region = c.Region
|
client.region = c.Region
|
||||||
|
|
||||||
log.Println("[INFO] Building AWS auth structure")
|
log.Println("[INFO] Building AWS auth structure")
|
||||||
creds := GetCredentials(c)
|
creds, err := GetCredentials(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// Call Get to check for credential provider. If nothing found, we'll get an
|
// Call Get to check for credential provider. If nothing found, we'll get an
|
||||||
// error, and we can present it nicely to the user
|
// error, and we can present it nicely to the user
|
||||||
cp, err := creds.Get()
|
cp, err := creds.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
|
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
|
||||||
errs = append(errs, fmt.Errorf(`No valid credential sources found for AWS Provider.
|
return nil, errors.New(`No valid credential sources found for AWS Provider.
|
||||||
Please see https://terraform.io/docs/providers/aws/index.html for more information on
|
Please see https://terraform.io/docs/providers/aws/index.html for more information on
|
||||||
providing credentials for the AWS Provider`))
|
providing credentials for the AWS Provider`)
|
||||||
} else {
|
|
||||||
errs = append(errs, fmt.Errorf("Error loading credentials for AWS Provider: %s", err))
|
|
||||||
}
|
}
|
||||||
return nil, &multierror.Error{Errors: errs}
|
|
||||||
|
return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
|
log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
|
||||||
@ -216,8 +219,7 @@ func (c *Config) Client() (interface{}, error) {
|
|||||||
if !c.SkipCredsValidation {
|
if !c.SkipCredsValidation {
|
||||||
err = c.ValidateCredentials(client.stsconn)
|
err = c.ValidateCredentials(client.stsconn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
return nil, err
|
||||||
return nil, &multierror.Error{Errors: errs}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +232,7 @@ func (c *Config) Client() (interface{}, error) {
|
|||||||
|
|
||||||
authErr := c.ValidateAccountId(client.accountid)
|
authErr := c.ValidateAccountId(client.accountid)
|
||||||
if authErr != nil {
|
if authErr != nil {
|
||||||
errs = append(errs, authErr)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
client.apigateway = apigateway.New(sess)
|
client.apigateway = apigateway.New(sess)
|
||||||
@ -272,11 +274,6 @@ func (c *Config) Client() (interface{}, error) {
|
|||||||
client.snsconn = sns.New(sess)
|
client.snsconn = sns.New(sess)
|
||||||
client.sqsconn = sqs.New(sess)
|
client.sqsconn = sqs.New(sess)
|
||||||
client.ssmconn = ssm.New(sess)
|
client.ssmconn = ssm.New(sess)
|
||||||
}
|
|
||||||
|
|
||||||
if len(errs) > 0 {
|
|
||||||
return nil, &multierror.Error{Errors: errs}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &client, nil
|
return &client, nil
|
||||||
}
|
}
|
||||||
@ -321,7 +318,7 @@ func (c *Config) ValidateAccountId(accountId string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] Validating account ID")
|
log.Println("[INFO] Validating account ID")
|
||||||
|
|
||||||
if c.ForbiddenAccountIds != nil {
|
if c.ForbiddenAccountIds != nil {
|
||||||
for _, id := range c.ForbiddenAccountIds {
|
for _, id := range c.ForbiddenAccountIds {
|
||||||
|
@ -3,6 +3,7 @@ package aws
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
"github.com/hashicorp/terraform/helper/mutexkv"
|
"github.com/hashicorp/terraform/helper/mutexkv"
|
||||||
@ -18,42 +19,44 @@ func Provider() terraform.ResourceProvider {
|
|||||||
// The actual provider
|
// The actual provider
|
||||||
return &schema.Provider{
|
return &schema.Provider{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"access_key": &schema.Schema{
|
"access_key": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["access_key"],
|
Description: descriptions["access_key"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"secret_key": &schema.Schema{
|
"secret_key": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["secret_key"],
|
Description: descriptions["secret_key"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"profile": &schema.Schema{
|
"profile": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["profile"],
|
Description: descriptions["profile"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"shared_credentials_file": &schema.Schema{
|
"assume_role": assumeRoleSchema(),
|
||||||
|
|
||||||
|
"shared_credentials_file": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["shared_credentials_file"],
|
Description: descriptions["shared_credentials_file"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"token": &schema.Schema{
|
"token": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["token"],
|
Description: descriptions["token"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"region": &schema.Schema{
|
"region": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
|
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
|
||||||
@ -64,14 +67,14 @@ func Provider() terraform.ResourceProvider {
|
|||||||
InputDefault: "us-east-1",
|
InputDefault: "us-east-1",
|
||||||
},
|
},
|
||||||
|
|
||||||
"max_retries": &schema.Schema{
|
"max_retries": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: 11,
|
Default: 11,
|
||||||
Description: descriptions["max_retries"],
|
Description: descriptions["max_retries"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"allowed_account_ids": &schema.Schema{
|
"allowed_account_ids": {
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
Optional: true,
|
Optional: true,
|
||||||
@ -79,7 +82,7 @@ func Provider() terraform.ResourceProvider {
|
|||||||
Set: schema.HashString,
|
Set: schema.HashString,
|
||||||
},
|
},
|
||||||
|
|
||||||
"forbidden_account_ids": &schema.Schema{
|
"forbidden_account_ids": {
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
Optional: true,
|
Optional: true,
|
||||||
@ -87,14 +90,14 @@ func Provider() terraform.ResourceProvider {
|
|||||||
Set: schema.HashString,
|
Set: schema.HashString,
|
||||||
},
|
},
|
||||||
|
|
||||||
"dynamodb_endpoint": &schema.Schema{
|
"dynamodb_endpoint": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["dynamodb_endpoint"],
|
Description: descriptions["dynamodb_endpoint"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"kinesis_endpoint": &schema.Schema{
|
"kinesis_endpoint": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
@ -103,35 +106,35 @@ func Provider() terraform.ResourceProvider {
|
|||||||
|
|
||||||
"endpoints": endpointsSchema(),
|
"endpoints": endpointsSchema(),
|
||||||
|
|
||||||
"insecure": &schema.Schema{
|
"insecure": {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: false,
|
Default: false,
|
||||||
Description: descriptions["insecure"],
|
Description: descriptions["insecure"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"skip_credentials_validation": &schema.Schema{
|
"skip_credentials_validation": {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: false,
|
Default: false,
|
||||||
Description: descriptions["skip_credentials_validation"],
|
Description: descriptions["skip_credentials_validation"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"skip_requesting_account_id": &schema.Schema{
|
"skip_requesting_account_id": {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: false,
|
Default: false,
|
||||||
Description: descriptions["skip_requesting_account_id"],
|
Description: descriptions["skip_requesting_account_id"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"skip_metadata_api_check": &schema.Schema{
|
"skip_metadata_api_check": {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: false,
|
Default: false,
|
||||||
Description: descriptions["skip_metadata_api_check"],
|
Description: descriptions["skip_metadata_api_check"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"s3_force_path_style": &schema.Schema{
|
"s3_force_path_style": {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: false,
|
Default: false,
|
||||||
@ -393,6 +396,14 @@ func init() {
|
|||||||
"i.e., http://s3.amazonaws.com/BUCKET/KEY. By default, the S3 client will\n" +
|
"i.e., http://s3.amazonaws.com/BUCKET/KEY. By default, the S3 client will\n" +
|
||||||
"use virtual hosted bucket addressing when possible\n" +
|
"use virtual hosted bucket addressing when possible\n" +
|
||||||
"(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.",
|
"(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.",
|
||||||
|
|
||||||
|
"assume_role_role_arn": "The ARN of an IAM role to assume prior to making API calls.",
|
||||||
|
|
||||||
|
"assume_role_session_name": "The session name to use when assuming the role. If ommitted," +
|
||||||
|
" no session name is passed to the AssumeRole call.",
|
||||||
|
|
||||||
|
"assume_role_external_id": "The external ID to use when assuming the role. If ommitted," +
|
||||||
|
" no external ID is passed to the AssumeRole call.",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,6 +425,18 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
|||||||
S3ForcePathStyle: d.Get("s3_force_path_style").(bool),
|
S3ForcePathStyle: d.Get("s3_force_path_style").(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assumeRoleList := d.Get("assume_role").(*schema.Set).List()
|
||||||
|
if len(assumeRoleList) == 1 {
|
||||||
|
assumeRole := assumeRoleList[0].(map[string]interface{})
|
||||||
|
config.AssumeRoleARN = assumeRole["role_arn"].(string)
|
||||||
|
config.AssumeRoleSessionName = assumeRole["session_name"].(string)
|
||||||
|
config.AssumeRoleExternalID = assumeRole["external_id"].(string)
|
||||||
|
log.Printf("[INFO] assume_role configuration set: (ARN: %q, SessionID: %q, ExternalID: %q)",
|
||||||
|
config.AssumeRoleARN, config.AssumeRoleSessionName, config.AssumeRoleExternalID)
|
||||||
|
} else {
|
||||||
|
log.Printf("[INFO] No assume_role block read from configuration")
|
||||||
|
}
|
||||||
|
|
||||||
endpointsSet := d.Get("endpoints").(*schema.Set)
|
endpointsSet := d.Get("endpoints").(*schema.Set)
|
||||||
|
|
||||||
for _, endpointsSetI := range endpointsSet.List() {
|
for _, endpointsSetI := range endpointsSet.List() {
|
||||||
@ -438,33 +461,72 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
|||||||
// This is a global MutexKV for use within this plugin.
|
// This is a global MutexKV for use within this plugin.
|
||||||
var awsMutexKV = mutexkv.NewMutexKV()
|
var awsMutexKV = mutexkv.NewMutexKV()
|
||||||
|
|
||||||
|
func assumeRoleSchema() *schema.Schema {
|
||||||
|
return &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
MaxItems: 1,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"role_arn": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: descriptions["assume_role_role_arn"],
|
||||||
|
},
|
||||||
|
|
||||||
|
"session_name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: descriptions["assume_role_session_name"],
|
||||||
|
},
|
||||||
|
|
||||||
|
"external_id": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: descriptions["assume_role_external_id"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Set: assumeRoleToHash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assumeRoleToHash(v interface{}) int {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
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)))
|
||||||
|
return hashcode.String(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
func endpointsSchema() *schema.Schema {
|
func endpointsSchema() *schema.Schema {
|
||||||
return &schema.Schema{
|
return &schema.Schema{
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"iam": &schema.Schema{
|
"iam": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["iam_endpoint"],
|
Description: descriptions["iam_endpoint"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"ec2": &schema.Schema{
|
"ec2": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["ec2_endpoint"],
|
Description: descriptions["ec2_endpoint"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"elb": &schema.Schema{
|
"elb": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["elb_endpoint"],
|
Description: descriptions["elb_endpoint"],
|
||||||
},
|
},
|
||||||
"s3": &schema.Schema{
|
"s3": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
|
@ -60,7 +60,7 @@ func s3Factory(conf map[string]string) (Client, error) {
|
|||||||
kmsKeyID := conf["kms_key_id"]
|
kmsKeyID := conf["kms_key_id"]
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
creds := terraformAws.GetCredentials(&terraformAws.Config{
|
creds, err := terraformAws.GetCredentials(&terraformAws.Config{
|
||||||
AccessKey: conf["access_key"],
|
AccessKey: conf["access_key"],
|
||||||
SecretKey: conf["secret_key"],
|
SecretKey: conf["secret_key"],
|
||||||
Token: conf["token"],
|
Token: conf["token"],
|
||||||
@ -69,7 +69,7 @@ func s3Factory(conf map[string]string) (Client, error) {
|
|||||||
})
|
})
|
||||||
// Call Get to check for credential provider. If nothing found, we'll get an
|
// Call Get to check for credential provider. If nothing found, we'll get an
|
||||||
// error, and we can present it nicely to the user
|
// error, and we can present it nicely to the user
|
||||||
_, err := creds.Get()
|
_, err = creds.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
|
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
|
||||||
errs = append(errs, fmt.Errorf(`No valid credential sources found for AWS S3 remote.
|
errs = append(errs, fmt.Errorf(`No valid credential sources found for AWS S3 remote.
|
||||||
|
@ -111,6 +111,23 @@ You can provide custom metadata API endpoint via `AWS_METADATA_ENDPOINT` variabl
|
|||||||
which expects the endpoint URL including the version
|
which expects the endpoint URL including the version
|
||||||
and defaults to `http://169.254.169.254:80/latest`.
|
and defaults to `http://169.254.169.254:80/latest`.
|
||||||
|
|
||||||
|
###Assume role
|
||||||
|
|
||||||
|
If provided with a role ARN, Terraform will attempt to assume this role
|
||||||
|
using the supplied credentials.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
provider "aws" {
|
||||||
|
assume_role {
|
||||||
|
role_arn = "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME"
|
||||||
|
session_name = "SESSION_NAME"
|
||||||
|
external_id = "EXTERNAL_ID"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Argument Reference
|
## Argument Reference
|
||||||
|
|
||||||
The following arguments are supported in the `provider` block:
|
The following arguments are supported in the `provider` block:
|
||||||
@ -130,6 +147,9 @@ The following arguments are supported in the `provider` block:
|
|||||||
* `profile` - (Optional) This is the AWS profile name as set in the shared credentials
|
* `profile` - (Optional) This is the AWS profile name as set in the shared credentials
|
||||||
file.
|
file.
|
||||||
|
|
||||||
|
* `assume_role` - (Optional) An `assume_role` block (documented below).`Only one
|
||||||
|
`assume_role` block may be in the configuration.
|
||||||
|
|
||||||
* `shared_credentials_file` = (Optional) This is the path to the shared credentials file.
|
* `shared_credentials_file` = (Optional) This is the path to the shared credentials file.
|
||||||
If this is not set and a profile is specified, ~/.aws/credentials will be used.
|
If this is not set and a profile is specified, ~/.aws/credentials will be used.
|
||||||
|
|
||||||
@ -187,7 +207,17 @@ The following arguments are supported in the `provider` block:
|
|||||||
S3 client will use virtual hosted bucket addressing when possible
|
S3 client will use virtual hosted bucket addressing when possible
|
||||||
(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.
|
(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.
|
||||||
|
|
||||||
Nested `endpoints` block supports the followings:
|
The nested `assume_role` block supports the following:
|
||||||
|
|
||||||
|
* `role_arn` - (Required) The ARN of the role to assume.
|
||||||
|
|
||||||
|
* `session_name` - (Optional) The session name to use when making the
|
||||||
|
AssumeRole call.
|
||||||
|
|
||||||
|
* `external_id` - (Optional) The external ID to use when making the
|
||||||
|
AssumeRole call.
|
||||||
|
|
||||||
|
Nested `endpoints` block supports the following:
|
||||||
|
|
||||||
* `iam` - (Optional) Use this to override the default endpoint
|
* `iam` - (Optional) Use this to override the default endpoint
|
||||||
URL constructed from the `region`. It's typically used to connect to
|
URL constructed from the `region`. It's typically used to connect to
|
||||||
|
Loading…
Reference in New Issue
Block a user