Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

266 aws native config #801

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 63 additions & 12 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
package aws

import (
"os"

"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
goamz "github.com/mitchellh/goamz/aws"
)

// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
// TODO: Move the validation to this, requires conditional schemas
// TODO: Move the configuration to this, requires validation

auth := awsAuthSource{}

return &schema.Provider{
Schema: map[string]*schema.Schema{
"access_key": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
"AWS_ACCESS_KEY",
"AWS_ACCESS_KEY_ID",
}, nil),
Type: schema.TypeString,
Required: true,
DefaultFunc: auth.accessKeyResolver(),
Description: descriptions["access_key"],
},

"secret_key": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
"AWS_SECRET_KEY",
"AWS_SECRET_ACCESS_KEY",
}, nil),
Type: schema.TypeString,
Required: true,
DefaultFunc: auth.secretKeyResolver(),
Description: descriptions["secret_key"],
},

Expand Down Expand Up @@ -88,6 +87,58 @@ func init() {
}
}

type awsAuthSource struct {
conventionalAuth goamz.Auth
attemptedConventionalAuth bool
}

func (a *awsAuthSource) awsSourcedAuth() goamz.Auth {
if a.attemptedConventionalAuth == false {
auth, err := goamz.EnvAuth()

if err != nil {
auth, _ = goamz.SharedAuth()
}

a.conventionalAuth = auth
a.attemptedConventionalAuth = true
}

return a.conventionalAuth
}

func (a *awsAuthSource) accessKeyResolver() schema.SchemaDefaultFunc {
return func() (interface{}, error) {
// NOTE: Do not remove this until AWS_ACCESS_KEY support has been removed
// https://github.com/hashicorp/terraform/issues/866
if accessKey := os.Getenv("AWS_ACCESS_KEY"); accessKey != "" {
return accessKey, nil
}

if auth := a.awsSourcedAuth(); auth.AccessKey != "" {
return auth.AccessKey, nil
}

return nil, nil
}
}

func (a *awsAuthSource) secretKeyResolver() schema.SchemaDefaultFunc {
return func() (interface{}, error) {
// NOTE: Do not remove this until AWS_SECRET_KEY support has been removed
// https://github.com/hashicorp/terraform/issues/866
if secretKey := os.Getenv("AWS_SECRET_KEY"); secretKey != "" {
return secretKey, nil
}

if auth := a.awsSourcedAuth(); auth.SecretKey != "" {
return auth.SecretKey, nil
}

return nil, nil
}
}

func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := Config{
AccessKey: d.Get("access_key").(string),
Expand Down
148 changes: 148 additions & 0 deletions builtin/providers/aws/provider_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package aws

import (
"fmt"
"io/ioutil"
"log"
"os"
"testing"
Expand Down Expand Up @@ -29,6 +31,152 @@ func TestProvider_impl(t *testing.T) {
var _ terraform.ResourceProvider = Provider()
}

func prepareFakeCredentialFile(access_key_id string, secret_key string) (*os.File, error) {
credentialFile, err := ioutil.TempFile("", "aws_credential_test")

if err != nil {
return nil, err
}

contents := fmt.Sprintf(`[default]
aws_access_key_id = %s
aws_secret_access_key = %s
`, access_key_id, secret_key)

credentialFile.Write([]byte(fmt.Sprintf(contents)))
credentialFile.Close()

return credentialFile, nil

}

func cleanAwsEnvConfig() func() {
oldAccessKey := os.Getenv("AWS_ACCESS_KEY")
oldSecretKey := os.Getenv("AWS_SECRET_KEY")
oldAccessKeyId := os.Getenv("AWS_ACCESS_KEY_ID")
oldSecretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")

os.Setenv("AWS_ACCESS_KEY", "")
os.Setenv("AWS_SECRET_KEY", "")
os.Setenv("AWS_ACCESS_KEY_ID", "")
os.Setenv("AWS_SECRET_ACCESS_KEY", "")

return func() {
os.Setenv("AWS_ACCESS_KEY", oldAccessKey)
os.Setenv("AWS_SECRET_KEY", oldSecretKey)
os.Setenv("AWS_ACCESS_KEY_ID", oldAccessKeyId)
os.Setenv("AWS_SECRET_ACCESS_KEY", oldSecretAccessKey)
}
}

func TestEnvVarsOverrideCredentialsFile(t *testing.T) {
resetEnvVars := cleanAwsEnvConfig()
defer resetEnvVars()

os.Setenv("AWS_ACCESS_KEY_ID", "access_key_id_from_aws_cli_env_var")
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret_access_key_from_aws_cli_env_var")

credentialFile, err := prepareFakeCredentialFile("access_key_id_from_config", "secret_access_key_from_config")

if err != nil {
t.Fatalf("Could not create temporary AWS config file: '%s'", err)
}

os.Setenv("AWS_CREDENTIAL_FILE", credentialFile.Name())
defer os.Remove(credentialFile.Name())

auth := awsAuthSource{}

access_key, _ := auth.accessKeyResolver()()

if access_key != "access_key_id_from_aws_cli_env_var" {
t.Errorf("expected: %s, got: %#v", "access_key_id_from_aws_cli_env_var", access_key)
}

secret_key, _ := auth.secretKeyResolver()()

if secret_key != "secret_access_key_from_aws_cli_env_var" {
t.Errorf("expected: %#v, got: %#v", "secret_access_key_from_aws_cli_env_var", secret_key)
}
}

// See:
// - https://github.com/hashicorp/terraform/pull/851
// - https://github.com/hashicorp/terraform/issues/866
//
// We should not change default behaviour in a minor release, if end-user has both variations
// of the env key set in their environment then we should give preference to the legacy one.
func TestDeprecatedEnvVarsOverrideOfficialOnes(t *testing.T) {
resetEnvVars := cleanAwsEnvConfig()
defer resetEnvVars()

os.Setenv("AWS_ACCESS_KEY", "access_key_id_from_legacy_env_var")
os.Setenv("AWS_SECRET_KEY", "secret_access_key_from_legacy_env_var")
os.Setenv("AWS_ACCESS_KEY_ID", "access_key_id_from_aws_cli_env_var")
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret_access_key_from_aws_cli_env_var")

auth := awsAuthSource{}

access_key, _ := auth.accessKeyResolver()()

if access_key != "access_key_id_from_legacy_env_var" {
t.Errorf("expected: %s, got: %#v", "access_key_id_from_legacy_env_var", access_key)
}

secret_key, _ := auth.secretKeyResolver()()

if secret_key != "secret_access_key_from_legacy_env_var" {
t.Errorf("expected: %s, got: %#v", "secret_access_key_from_legacy_env_var", secret_key)
}
}

func TestLoadCredentialsFromFileWhenNoConfigInEnv(t *testing.T) {
resetEnvVars := cleanAwsEnvConfig()
defer resetEnvVars()

credentialFile, err := prepareFakeCredentialFile("access_key_id_from_config", "secret_access_key_from_config")

if err != nil {
t.Fatalf("Could not create temporary AWS config file: '%s'", err)
}

os.Setenv("AWS_CREDENTIAL_FILE", credentialFile.Name())
defer os.Remove(credentialFile.Name())

auth := awsAuthSource{}

access_key, _ := auth.accessKeyResolver()()

if access_key != "access_key_id_from_config" {
t.Errorf("expected: %s, got: %#v", "access_key_id_from_config", access_key)
}

secret_key, _ := auth.secretKeyResolver()()

if secret_key != "secret_access_key_from_config" {
t.Errorf("expected: %s, bad: %#v", "secret_access_key_from_config", secret_key)
}
}

func TestAuthSourcerReturnsNilWhenDefaultsCannotBeFound(t *testing.T) {
resetEnvVars := cleanAwsEnvConfig()
defer resetEnvVars()

auth := awsAuthSource{}

access_key, _ := auth.accessKeyResolver()()

if access_key != nil {
t.Errorf("expected: nil, got: %#v", access_key)
}

secret_key, _ := auth.secretKeyResolver()()

if secret_key != nil {
t.Errorf("expected: nil, got: %#v", secret_key)
}
}

func testAccPreCheck(t *testing.T) {
if v := os.Getenv("AWS_ACCESS_KEY_ID"); v == "" {
t.Fatal("AWS_ACCESS_KEY_ID must be set for acceptance tests")
Expand Down
10 changes: 8 additions & 2 deletions website/source/docs/providers/aws/index.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,16 @@ resource "aws_instance" "web" {
The following arguments are supported:

* `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.
it can also be sourced from the `AWS_ACCESS_KEY_ID` environment variable, or the
[AWS cli credentials file](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files).

* `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.
it can also be sourced from the `AWS_SECRET_ACCESS_KEY` environment variable, or the
[AWS cli credentials file](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files).

* `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's possible to load credentials from a [specific profile](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-multiple-profiles)
within the AWS cli's credentials file by setting the `AWS_PROFILE` environment variable.
The `default` profile will be used if this variable is not set.