@@ -22,6 +22,21 @@ import (
2222 "github.com/hashicorp/go-multierror"
2323)
2424
25+ const (
26+ // errMsgNoValidCredentialSources error getting credentials
27+ errMsgNoValidCredentialSources = `No valid credential sources found for AWS Provider.
28+ Please see https://terraform.io/docs/providers/aws/index.html for more information on
29+ providing credentials for the AWS Provider`
30+ )
31+
32+ var (
33+ // ErrNoValidCredentialSources indicates that no credentials source could be found
34+ ErrNoValidCredentialSources = errNoValidCredentialSources ()
35+ )
36+
37+ func errNoValidCredentialSources () error { return errors .New (errMsgNoValidCredentialSources ) }
38+
39+ // GetAccountIDAndPartition gets the account ID and associated partition.
2540func GetAccountIDAndPartition (iamconn * iam.IAM , stsconn * sts.STS , authProviderName string ) (string , string , error ) {
2641 var accountID , partition string
2742 var err , errors error
@@ -51,6 +66,8 @@ func GetAccountIDAndPartition(iamconn *iam.IAM, stsconn *sts.STS, authProviderNa
5166 return accountID , partition , errors
5267}
5368
69+ // GetAccountIDAndPartitionFromEC2Metadata gets the account ID and associated
70+ // partition from EC2 metadata.
5471func GetAccountIDAndPartitionFromEC2Metadata () (string , string , error ) {
5572 log .Println ("[DEBUG] Trying to get account information via EC2 Metadata" )
5673
@@ -75,6 +92,8 @@ func GetAccountIDAndPartitionFromEC2Metadata() (string, string, error) {
7592 return parseAccountIDAndPartitionFromARN (info .InstanceProfileArn )
7693}
7794
95+ // GetAccountIDAndPartitionFromIAMGetUser gets the account ID and associated
96+ // partition from IAM.
7897func GetAccountIDAndPartitionFromIAMGetUser (iamconn * iam.IAM ) (string , string , error ) {
7998 log .Println ("[DEBUG] Trying to get account information via iam:GetUser" )
8099
@@ -102,6 +121,8 @@ func GetAccountIDAndPartitionFromIAMGetUser(iamconn *iam.IAM) (string, string, e
102121 return parseAccountIDAndPartitionFromARN (aws .StringValue (output .User .Arn ))
103122}
104123
124+ // GetAccountIDAndPartitionFromIAMListRoles gets the account ID and associated
125+ // partition from listing IAM roles.
105126func GetAccountIDAndPartitionFromIAMListRoles (iamconn * iam.IAM ) (string , string , error ) {
106127 log .Println ("[DEBUG] Trying to get account information via iam:ListRoles" )
107128
@@ -123,6 +144,8 @@ func GetAccountIDAndPartitionFromIAMListRoles(iamconn *iam.IAM) (string, string,
123144 return parseAccountIDAndPartitionFromARN (aws .StringValue (output .Roles [0 ].Arn ))
124145}
125146
147+ // GetAccountIDAndPartitionFromSTSGetCallerIdentity gets the account ID and associated
148+ // partition from STS caller identity.
126149func GetAccountIDAndPartitionFromSTSGetCallerIdentity (stsconn * sts.STS ) (string , string , error ) {
127150 log .Println ("[DEBUG] Trying to get account information via sts:GetCallerIdentity" )
128151
@@ -148,9 +171,54 @@ func parseAccountIDAndPartitionFromARN(inputARN string) (string, string, error)
148171 return arn .AccountID , arn .Partition , nil
149172}
150173
151- // This function is responsible for reading credentials from the
152- // environment in the case that they're not explicitly specified
153- // in the Terraform configuration.
174+ // GetCredentialsFromSession returns credentials derived from a session. A
175+ // session uses the AWS SDK Go chain of providers so may use a provider (e.g.,
176+ // ProcessProvider) that is not part of the Terraform provider chain.
177+ func GetCredentialsFromSession (c * Config ) (* awsCredentials.Credentials , error ) {
178+ log .Printf ("[INFO] Attempting to use session-derived credentials" )
179+
180+ var sess * session.Session
181+ var err error
182+ if c .Profile == "" {
183+ sess , err = session .NewSession ()
184+ if err != nil {
185+ return nil , ErrNoValidCredentialSources
186+ }
187+ } else {
188+ options := & session.Options {
189+ Config : aws.Config {
190+ HTTPClient : cleanhttp .DefaultClient (),
191+ MaxRetries : aws .Int (0 ),
192+ Region : aws .String (c .Region ),
193+ },
194+ }
195+ options .Profile = c .Profile
196+ options .SharedConfigState = session .SharedConfigEnable
197+
198+ sess , err = session .NewSessionWithOptions (* options )
199+ if err != nil {
200+ if IsAWSErr (err , "NoCredentialProviders" , "" ) {
201+ return nil , ErrNoValidCredentialSources
202+ }
203+ return nil , fmt .Errorf ("Error creating AWS session: %s" , err )
204+ }
205+ }
206+
207+ creds := sess .Config .Credentials
208+ cp , err := sess .Config .Credentials .Get ()
209+ if err != nil {
210+ return nil , ErrNoValidCredentialSources
211+ }
212+
213+ log .Printf ("[INFO] Successfully derived credentials from session" )
214+ log .Printf ("[INFO] AWS Auth provider used: %q" , cp .ProviderName )
215+ return creds , nil
216+ }
217+
218+ // GetCredentials gets credentials from the environment, shared credentials,
219+ // or the session (which may include a credential process). GetCredentials also
220+ // validates the credentials and the ability to assume a role or will return an
221+ // error if unsuccessful.
154222func GetCredentials (c * Config ) (* awsCredentials.Credentials , error ) {
155223 // build a chain provider, lazy-evaluated by aws-sdk
156224 providers := []awsCredentials.Provider {
@@ -225,30 +293,32 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) {
225293 }
226294 }
227295
296+ // Validate the credentials before returning them
297+ creds := awsCredentials .NewChainCredentials (providers )
298+ cp , err := creds .Get ()
299+ if err != nil {
300+ if IsAWSErr (err , "NoCredentialProviders" , "" ) {
301+ creds , err = GetCredentialsFromSession (c )
302+ if err != nil {
303+ return nil , err
304+ }
305+ } else {
306+ return nil , fmt .Errorf ("Error loading credentials for AWS Provider: %s" , err )
307+ }
308+ } else {
309+ log .Printf ("[INFO] AWS Auth provider used: %q" , cp .ProviderName )
310+ }
311+
228312 // This is the "normal" flow (i.e. not assuming a role)
229313 if c .AssumeRoleARN == "" {
230- return awsCredentials . NewChainCredentials ( providers ) , nil
314+ return creds , nil
231315 }
232316
233317 // Otherwise we need to construct an STS client with the main credentials, and verify
234318 // that we can assume the defined role.
235319 log .Printf ("[INFO] Attempting to AssumeRole %s (SessionName: %q, ExternalId: %q, Policy: %q)" ,
236320 c .AssumeRoleARN , c .AssumeRoleSessionName , c .AssumeRoleExternalID , c .AssumeRolePolicy )
237321
238- creds := awsCredentials .NewChainCredentials (providers )
239- cp , err := creds .Get ()
240- if err != nil {
241- if awsErr , ok := err .(awserr.Error ); ok && awsErr .Code () == "NoCredentialProviders" {
242- return nil , errors .New (`No valid credential sources found for AWS Provider.
243- Please see https://terraform.io/docs/providers/aws/index.html for more information on
244- providing credentials for the AWS Provider` )
245- }
246-
247- return nil , fmt .Errorf ("Error loading credentials for AWS Provider: %s" , err )
248- }
249-
250- log .Printf ("[INFO] AWS Auth provider used: %q" , cp .ProviderName )
251-
252322 awsConfig := & aws.Config {
253323 Credentials : creds ,
254324 Region : aws .String (c .Region ),
0 commit comments