Skip to main content

Fun times with AWS STS AssumeRole

I had an AWS STS AssumeRole problem today when I was working with the AWS SDK; digging in it turned out the problem was that AssumeRole was not actually being invoked. Poking around I was unsurprised to find that @ben11kehoe had already clearly stated the problem and GitHub user petitout had example Go code with the right way to do it (link).

Sometimes you need to assume another role than your default execution role in order to accomplish a task. You can explicitly call STS AssumeRole yourself and configure the SDK client to use the returned credentials, or you can configure your SDK client to call AssumeRole itself when it needs the credentials. I like the second way because it means that credentials aren’t something that my code has access to.

Sample code for not assuming a role correctly; you might have stumbled on this the same way I did:

// almost certainly not what you want
cfg, _ := config.LoadDefaultConfig(
	ctx,
	config.WithAssumeRoleCredentialOptions(func(aro *stscreds.AssumeRoleOptions) {
		aro.Client = stsClient
		aro.RoleARN = arn
		aro.RoleSessionName = sessionName
	}),
)

A person might think that the code above is right, because it has all kinds of the right keywords that trigger pattern recognition. The comments on WithAssumeRoleCredentialOptions read:

// WithAssumeRoleCredentialOptions is a helper function to construct functional
// options that sets a function to use stscreds.AssumeRoleOptions on config's
// LoadOptions. If assume role credentials options is set to nil, the assume
// role credentials value will be ignored. If multiple WithAssumeRoleCredentialOptions
// calls are made, the last call overrides the previous call values.

I think that what this actually does is … you know, I truly have no idea, and don’t intend to dig into what exactly this does. It definitely does not do what I thought it did.

Sample code for probably doing what you want:

// almost certainly what you do want instead
cfg, err := config.LoadDefaultConfig(
	ctx, 
	config.WithCredentialsProvider(stscreds.NewAssumeRoleProvider(
		stsClient, arn, func(aro *stscreds.AssumeRoleOptions) {
			aro.RoleSessionName = sessionName
		}
	))
)

When the SDK needs credentials, this setup uses STS AssumeRole to obtain credentials with the STS client and role ARN that you provide. The stsClient in this example can be configured to take credentials from the environment.

Hope posting this helps make the solution findable for other folks.

re-posting and expanding on my original Mastodon post