mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-24 15:36:26 -06:00
Merge pull request #4632 from hashicorp/f-aws-profile
provider/aws: Add profile to provider config
This commit is contained in:
commit
6acc7a14e4
@ -48,11 +48,13 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
Token string
|
||||
Region string
|
||||
MaxRetries int
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
CredsFilename string
|
||||
Profile string
|
||||
Token string
|
||||
Region string
|
||||
MaxRetries int
|
||||
|
||||
AllowedAccountIds []interface{}
|
||||
ForbiddenAccountIds []interface{}
|
||||
@ -113,7 +115,7 @@ func (c *Config) Client() (interface{}, error) {
|
||||
client.region = c.Region
|
||||
|
||||
log.Println("[INFO] Building AWS auth structure")
|
||||
creds := getCreds(c.AccessKey, c.SecretKey, c.Token)
|
||||
creds := getCreds(c.AccessKey, c.SecretKey, c.Token, c.Profile, c.CredsFilename)
|
||||
// Call Get to check for credential provider. If nothing found, we'll get an
|
||||
// error, and we can present it nicely to the user
|
||||
_, err = creds.Get()
|
||||
@ -341,7 +343,7 @@ func (c *Config) ValidateAccountId(iamconn *iam.IAM) error {
|
||||
// This function is responsible for reading credentials from the
|
||||
// environment in the case that they're not explicitly specified
|
||||
// in the Terraform configuration.
|
||||
func getCreds(key, secret, token string) *awsCredentials.Credentials {
|
||||
func getCreds(key, secret, token, profile, credsfile string) *awsCredentials.Credentials {
|
||||
// build a chain provider, lazy-evaulated by aws-sdk
|
||||
providers := []awsCredentials.Provider{
|
||||
&awsCredentials.StaticProvider{Value: awsCredentials.Value{
|
||||
@ -350,7 +352,10 @@ func getCreds(key, secret, token string) *awsCredentials.Credentials {
|
||||
SessionToken: token,
|
||||
}},
|
||||
&awsCredentials.EnvProvider{},
|
||||
&awsCredentials.SharedCredentialsProvider{},
|
||||
&awsCredentials.SharedCredentialsProvider{
|
||||
Filename: credsfile,
|
||||
Profile: profile,
|
||||
},
|
||||
}
|
||||
|
||||
// We only look in the EC2 metadata API if we can connect
|
||||
|
@ -3,6 +3,7 @@ package aws
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@ -16,7 +17,7 @@ func TestAWSConfig_shouldError(t *testing.T) {
|
||||
defer resetEnv()
|
||||
cfg := Config{}
|
||||
|
||||
c := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token)
|
||||
c := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
||||
_, err := c.Get()
|
||||
if awsErr, ok := err.(awserr.Error); ok {
|
||||
if awsErr.Code() != "NoCredentialProviders" {
|
||||
@ -49,7 +50,7 @@ func TestAWSConfig_shouldBeStatic(t *testing.T) {
|
||||
Token: c.Token,
|
||||
}
|
||||
|
||||
creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token)
|
||||
creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
||||
if creds == nil {
|
||||
t.Fatalf("Expected a static creds provider to be returned")
|
||||
}
|
||||
@ -84,7 +85,7 @@ func TestAWSConfig_shouldIAM(t *testing.T) {
|
||||
// An empty config, no key supplied
|
||||
cfg := Config{}
|
||||
|
||||
creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token)
|
||||
creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
||||
if creds == nil {
|
||||
t.Fatalf("Expected a static creds provider to be returned")
|
||||
}
|
||||
@ -133,7 +134,7 @@ func TestAWSConfig_shouldIgnoreIAM(t *testing.T) {
|
||||
Token: c.Token,
|
||||
}
|
||||
|
||||
creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token)
|
||||
creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
||||
if creds == nil {
|
||||
t.Fatalf("Expected a static creds provider to be returned")
|
||||
}
|
||||
@ -153,15 +154,65 @@ func TestAWSConfig_shouldIgnoreIAM(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var credentialsFileContents = `[myprofile]
|
||||
aws_access_key_id = accesskey
|
||||
aws_secret_access_key = secretkey
|
||||
`
|
||||
|
||||
func TestAWSConfig_shouldBeShared(t *testing.T) {
|
||||
file, err := ioutil.TempFile(os.TempDir(), "terraform_aws_cred")
|
||||
if err != nil {
|
||||
t.Fatalf("Error writing temporary credentials file: %s", err)
|
||||
}
|
||||
_, err = file.WriteString(credentialsFileContents)
|
||||
if err != nil {
|
||||
t.Fatalf("Error writing temporary credentials to file: %s", err)
|
||||
}
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Error closing temporary credentials file: %s", err)
|
||||
}
|
||||
|
||||
defer os.Remove(file.Name())
|
||||
|
||||
resetEnv := unsetEnv(t)
|
||||
defer resetEnv()
|
||||
|
||||
if err := os.Setenv("AWS_PROFILE", "myprofile"); err != nil {
|
||||
t.Fatalf("Error resetting env var AWS_PROFILE: %s", err)
|
||||
}
|
||||
if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", file.Name()); err != nil {
|
||||
t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
|
||||
}
|
||||
|
||||
creds := getCreds("", "", "", "myprofile", file.Name())
|
||||
if creds == nil {
|
||||
t.Fatalf("Expected a provider chain to be returned")
|
||||
}
|
||||
v, err := creds.Get()
|
||||
if err != nil {
|
||||
t.Fatalf("Error gettings creds: %s", err)
|
||||
}
|
||||
|
||||
if v.AccessKeyID != "accesskey" {
|
||||
t.Fatalf("AccessKeyID mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID)
|
||||
}
|
||||
|
||||
if v.SecretAccessKey != "secretkey" {
|
||||
t.Fatalf("SecretAccessKey mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAWSConfig_shouldBeENV(t *testing.T) {
|
||||
// need to set the environment variables to a dummy string, as we don't know
|
||||
// what they may be at runtime without hardcoding here
|
||||
s := "some_env"
|
||||
resetEnv := setEnv(s, t)
|
||||
|
||||
defer resetEnv()
|
||||
|
||||
cfg := Config{}
|
||||
creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token)
|
||||
creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
||||
if creds == nil {
|
||||
t.Fatalf("Expected a static creds provider to be returned")
|
||||
}
|
||||
@ -195,6 +246,12 @@ func unsetEnv(t *testing.T) func() {
|
||||
if err := os.Unsetenv("AWS_SESSION_TOKEN"); err != nil {
|
||||
t.Fatalf("Error unsetting env var AWS_SESSION_TOKEN: %s", err)
|
||||
}
|
||||
if err := os.Unsetenv("AWS_PROFILE"); err != nil {
|
||||
t.Fatalf("Error unsetting env var AWS_TOKEN: %s", err)
|
||||
}
|
||||
if err := os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE"); err != nil {
|
||||
t.Fatalf("Error unsetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
|
||||
}
|
||||
|
||||
return func() {
|
||||
// re-set all the envs we unset above
|
||||
@ -207,6 +264,12 @@ func unsetEnv(t *testing.T) func() {
|
||||
if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil {
|
||||
t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err)
|
||||
}
|
||||
if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil {
|
||||
t.Fatalf("Error resetting env var AWS_PROFILE: %s", err)
|
||||
}
|
||||
if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", e.CredsFilename); err != nil {
|
||||
t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +285,12 @@ func setEnv(s string, t *testing.T) func() {
|
||||
if err := os.Setenv("AWS_SESSION_TOKEN", s); err != nil {
|
||||
t.Fatalf("Error setting env var AWS_SESSION_TOKEN: %s", err)
|
||||
}
|
||||
if err := os.Setenv("AWS_PROFILE", s); err != nil {
|
||||
t.Fatalf("Error setting env var AWS_PROFILE: %s", err)
|
||||
}
|
||||
if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil {
|
||||
t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err)
|
||||
}
|
||||
|
||||
return func() {
|
||||
// re-set all the envs we unset above
|
||||
@ -234,6 +303,12 @@ func setEnv(s string, t *testing.T) func() {
|
||||
if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil {
|
||||
t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err)
|
||||
}
|
||||
if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil {
|
||||
t.Fatalf("Error setting env var AWS_PROFILE: %s", err)
|
||||
}
|
||||
if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil {
|
||||
t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,15 +339,17 @@ func getEnv() *currentEnv {
|
||||
// Grab any existing AWS keys and preserve. In some tests we'll unset these, so
|
||||
// we need to have them and restore them after
|
||||
return ¤tEnv{
|
||||
Key: os.Getenv("AWS_ACCESS_KEY_ID"),
|
||||
Secret: os.Getenv("AWS_SECRET_ACCESS_KEY"),
|
||||
Token: os.Getenv("AWS_SESSION_TOKEN"),
|
||||
Key: os.Getenv("AWS_ACCESS_KEY_ID"),
|
||||
Secret: os.Getenv("AWS_SECRET_ACCESS_KEY"),
|
||||
Token: os.Getenv("AWS_SESSION_TOKEN"),
|
||||
Profile: os.Getenv("AWS_TOKEN"),
|
||||
CredsFilename: os.Getenv("AWS_SHARED_CREDENTIALS_FILE"),
|
||||
}
|
||||
}
|
||||
|
||||
// struct to preserve the current environment
|
||||
type currentEnv struct {
|
||||
Key, Secret, Token string
|
||||
Key, Secret, Token, Profile, CredsFilename string
|
||||
}
|
||||
|
||||
type routes struct {
|
||||
|
@ -29,6 +29,20 @@ func Provider() terraform.ResourceProvider {
|
||||
Description: descriptions["secret_key"],
|
||||
},
|
||||
|
||||
"profile": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "",
|
||||
Description: descriptions["profile"],
|
||||
},
|
||||
|
||||
"shared_credentials_file": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "",
|
||||
Description: descriptions["shared_credentials_file"],
|
||||
},
|
||||
|
||||
"token": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
@ -222,6 +236,12 @@ func init() {
|
||||
"secret_key": "The secret key for API operations. You can retrieve this\n" +
|
||||
"from the 'Security & Credentials' section of the AWS console.",
|
||||
|
||||
"profile": "The profile for API operations. If not set, the default profile\n" +
|
||||
"created with `aws configure` will be used.",
|
||||
|
||||
"shared_credentials_file": "The path to the shared credentials file. If not set\n" +
|
||||
"this defaults to ~/.aws/credentials.",
|
||||
|
||||
"token": "session token. A session token is only required if you are\n" +
|
||||
"using temporary security credentials.",
|
||||
|
||||
@ -241,6 +261,8 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||
config := Config{
|
||||
AccessKey: d.Get("access_key").(string),
|
||||
SecretKey: d.Get("secret_key").(string),
|
||||
Profile: d.Get("profile").(string),
|
||||
CredsFilename: d.Get("shared_credentials_file").(string),
|
||||
Token: d.Get("token").(string),
|
||||
Region: d.Get("region").(string),
|
||||
MaxRetries: d.Get("max_retries").(int),
|
||||
|
@ -34,14 +34,26 @@ resource "aws_instance" "web" {
|
||||
|
||||
The following arguments are supported in the `provider` block:
|
||||
|
||||
* `access_key` - (Required) This is the AWS access key. It must be provided, but
|
||||
it can also be sourced from the `AWS_ACCESS_KEY_ID` environment variable.
|
||||
* `access_key` - (Optional) This is the AWS access key. It must be provided, but
|
||||
it can also be sourced from the `AWS_ACCESS_KEY_ID` environment variable, or via
|
||||
a shared credentials file if `profile` is specified.
|
||||
|
||||
* `secret_key` - (Required) This is the AWS secret key. It must be provided, but
|
||||
it can also be sourced from the `AWS_SECRET_ACCESS_KEY` environment variable.
|
||||
* `secret_key` - (Optional) This is the AWS secret key. It must be provided, but
|
||||
it can also be sourced from the `AWS_SECRET_ACCESS_KEY` environment variable, or
|
||||
via a shared credentials file if `profile` is specified.
|
||||
|
||||
* `region` - (Required) This is the AWS region. It must be provided, but
|
||||
it can also be sourced from the `AWS_DEFAULT_REGION` environment variables.
|
||||
it can also be sourced from the `AWS_DEFAULT_REGION` environment variables, or
|
||||
via a shared credentials file if `profile` is specified.
|
||||
|
||||
* `profile` - (Optional) This is the AWS profile name as set in 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.
|
||||
|
||||
* `token` - (Optional) Use this to set an MFA token. It can also be sourced
|
||||
from the `AWS_SECURITY_TOKEN` environment variable.
|
||||
|
||||
* `max_retries` - (Optional) This is the maximum number of times an API call is
|
||||
being retried in case requests are being throttled or experience transient failures.
|
||||
@ -55,8 +67,10 @@ The following arguments are supported in the `provider` block:
|
||||
to prevent you mistakenly using a wrong one (and end up destroying live environment).
|
||||
Conflicts with `allowed_account_ids`.
|
||||
|
||||
* `dynamodb_endpoint` - (Optional) Use this to override the default endpoint URL constructed from the `region`. It's typically used to connect to dynamodb-local.
|
||||
* `dynamodb_endpoint` - (Optional) Use this to override the default endpoint
|
||||
URL constructed from the `region`. It's typically used to connect to
|
||||
dynamodb-local.
|
||||
|
||||
* `kinesis_endpoint` - (Optional) Use this to override the default endpoint URL constructed from the `region`. It's typically used to connect to kinesalite.
|
||||
* `kinesis_endpoint` - (Optional) Use this to override the default endpoint URL
|
||||
constructed from the `region`. It's typically used to connect to kinesalite.
|
||||
|
||||
* `token` - (Optional) Use this to set an MFA token. It can also be sourced from the `AWS_SECURITY_TOKEN` environment variable.
|
||||
|
Loading…
Reference in New Issue
Block a user