github.com/anfernee/terraform@v0.6.16-0.20160430000239-06e5085a92f2/builtin/providers/aws/auth_helpers.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "strings" 8 "time" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 awsCredentials "github.com/aws/aws-sdk-go/aws/credentials" 13 "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" 14 "github.com/aws/aws-sdk-go/aws/ec2metadata" 15 "github.com/aws/aws-sdk-go/aws/session" 16 "github.com/aws/aws-sdk-go/service/iam" 17 "github.com/hashicorp/go-cleanhttp" 18 ) 19 20 func GetAccountId(iamconn *iam.IAM, authProviderName string) (string, error) { 21 // If we have creds from instance profile, we can use metadata API 22 if authProviderName == ec2rolecreds.ProviderName { 23 log.Println("[DEBUG] Trying to get account ID via AWS Metadata API") 24 25 cfg := &aws.Config{} 26 setOptionalEndpoint(cfg) 27 metadataClient := ec2metadata.New(session.New(cfg)) 28 info, err := metadataClient.IAMInfo() 29 if err != nil { 30 // This can be triggered when no IAM Role is assigned 31 // or AWS just happens to return invalid response 32 return "", fmt.Errorf("Failed getting EC2 IAM info: %s", err) 33 } 34 35 return parseAccountIdFromArn(info.InstanceProfileArn) 36 } 37 38 // Then try IAM GetUser 39 log.Println("[DEBUG] Trying to get account ID via iam:GetUser") 40 outUser, err := iamconn.GetUser(nil) 41 if err == nil { 42 return parseAccountIdFromArn(*outUser.User.Arn) 43 } 44 45 // Then try IAM ListRoles 46 awsErr, ok := err.(awserr.Error) 47 // AccessDenied and ValidationError can be raised 48 // if credentials belong to federated profile, so we ignore these 49 if !ok || (awsErr.Code() != "AccessDenied" && awsErr.Code() != "ValidationError") { 50 return "", fmt.Errorf("Failed getting account ID via 'iam:GetUser': %s", err) 51 } 52 53 log.Printf("[DEBUG] Getting account ID via iam:GetUser failed: %s", err) 54 log.Println("[DEBUG] Trying to get account ID via iam:ListRoles instead") 55 outRoles, err := iamconn.ListRoles(&iam.ListRolesInput{ 56 MaxItems: aws.Int64(int64(1)), 57 }) 58 if err != nil { 59 return "", fmt.Errorf("Failed getting account ID via 'iam:ListRoles': %s", err) 60 } 61 62 if len(outRoles.Roles) < 1 { 63 return "", fmt.Errorf("Failed getting account ID via 'iam:ListRoles': No roles available") 64 } 65 66 return parseAccountIdFromArn(*outRoles.Roles[0].Arn) 67 } 68 69 func parseAccountIdFromArn(arn string) (string, error) { 70 parts := strings.Split(arn, ":") 71 if len(parts) < 5 { 72 return "", fmt.Errorf("Unable to parse ID from invalid ARN: %q", arn) 73 } 74 return parts[4], nil 75 } 76 77 // This function is responsible for reading credentials from the 78 // environment in the case that they're not explicitly specified 79 // in the Terraform configuration. 80 func GetCredentials(key, secret, token, profile, credsfile string) *awsCredentials.Credentials { 81 // build a chain provider, lazy-evaulated by aws-sdk 82 providers := []awsCredentials.Provider{ 83 &awsCredentials.StaticProvider{Value: awsCredentials.Value{ 84 AccessKeyID: key, 85 SecretAccessKey: secret, 86 SessionToken: token, 87 }}, 88 &awsCredentials.EnvProvider{}, 89 &awsCredentials.SharedCredentialsProvider{ 90 Filename: credsfile, 91 Profile: profile, 92 }, 93 } 94 95 // Build isolated HTTP client to avoid issues with globally-shared settings 96 client := cleanhttp.DefaultClient() 97 98 // Keep the timeout low as we don't want to wait in non-EC2 environments 99 client.Timeout = 100 * time.Millisecond 100 cfg := &aws.Config{ 101 HTTPClient: client, 102 } 103 usedEndpoint := setOptionalEndpoint(cfg) 104 105 // Real AWS should reply to a simple metadata request. 106 // We check it actually does to ensure something else didn't just 107 // happen to be listening on the same IP:Port 108 metadataClient := ec2metadata.New(session.New(cfg)) 109 if metadataClient.Available() { 110 providers = append(providers, &ec2rolecreds.EC2RoleProvider{ 111 Client: metadataClient, 112 }) 113 log.Printf("[INFO] AWS EC2 instance detected via default metadata" + 114 " API endpoint, EC2RoleProvider added to the auth chain") 115 } else { 116 if usedEndpoint == "" { 117 usedEndpoint = "default location" 118 } 119 log.Printf("[WARN] Ignoring AWS metadata API endpoint at %s "+ 120 "as it doesn't return any instance-id", usedEndpoint) 121 } 122 123 return awsCredentials.NewChainCredentials(providers) 124 } 125 126 func setOptionalEndpoint(cfg *aws.Config) string { 127 endpoint := os.Getenv("AWS_METADATA_URL") 128 if endpoint != "" { 129 log.Printf("[INFO] Setting custom metadata endpoint: %q", endpoint) 130 cfg.Endpoint = aws.String(endpoint) 131 return endpoint 132 } 133 return "" 134 }