mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-10 23:55:34 -06:00
This allows specification of the profile for the shared credentials provider for AWS to be specified in Terraform configuration. This is useful if defining providers with aliases, or if you don't want to set environment variables. Example: $ aws configure --profile this_is_dog ... enter keys $ cat main.tf provider "aws" { profile = "this_is_dog" # Optionally also specify the path to the credentials file shared_credentials_file = "/tmp/credentials" } This is equivalent to specifying AWS_PROFILE or AWS_SHARED_CREDENTIALS_FILE in the environment.
377 lines
11 KiB
Go
377 lines
11 KiB
Go
package aws
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
)
|
|
|
|
func TestAWSConfig_shouldError(t *testing.T) {
|
|
resetEnv := unsetEnv(t)
|
|
defer resetEnv()
|
|
cfg := Config{}
|
|
|
|
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" {
|
|
t.Fatalf("Expected NoCredentialProviders error")
|
|
}
|
|
}
|
|
if err == nil {
|
|
t.Fatalf("Expected an error with empty env, keys, and IAM in AWS Config")
|
|
}
|
|
}
|
|
|
|
func TestAWSConfig_shouldBeStatic(t *testing.T) {
|
|
simple := []struct {
|
|
Key, Secret, Token string
|
|
}{
|
|
{
|
|
Key: "test",
|
|
Secret: "secret",
|
|
}, {
|
|
Key: "test",
|
|
Secret: "test",
|
|
Token: "test",
|
|
},
|
|
}
|
|
|
|
for _, c := range simple {
|
|
cfg := Config{
|
|
AccessKey: c.Key,
|
|
SecretKey: c.Secret,
|
|
Token: c.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")
|
|
}
|
|
v, err := creds.Get()
|
|
if err != nil {
|
|
t.Fatalf("Error gettings creds: %s", err)
|
|
}
|
|
if v.AccessKeyID != c.Key {
|
|
t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID)
|
|
}
|
|
if v.SecretAccessKey != c.Secret {
|
|
t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey)
|
|
}
|
|
if v.SessionToken != c.Token {
|
|
t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestAWSConfig_shouldIAM is designed to test the scenario of running Terraform
|
|
// from an EC2 instance, without environment variables or manually supplied
|
|
// credentials.
|
|
func TestAWSConfig_shouldIAM(t *testing.T) {
|
|
// clear AWS_* environment variables
|
|
resetEnv := unsetEnv(t)
|
|
defer resetEnv()
|
|
|
|
// capture the test server's close method, to call after the test returns
|
|
ts := awsEnv(t)
|
|
defer ts()
|
|
|
|
// An empty config, no key supplied
|
|
cfg := Config{}
|
|
|
|
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")
|
|
}
|
|
|
|
v, err := creds.Get()
|
|
if err != nil {
|
|
t.Fatalf("Error gettings creds: %s", err)
|
|
}
|
|
if v.AccessKeyID != "somekey" {
|
|
t.Fatalf("AccessKeyID mismatch, expected: (somekey), got (%s)", v.AccessKeyID)
|
|
}
|
|
if v.SecretAccessKey != "somesecret" {
|
|
t.Fatalf("SecretAccessKey mismatch, expected: (somesecret), got (%s)", v.SecretAccessKey)
|
|
}
|
|
if v.SessionToken != "sometoken" {
|
|
t.Fatalf("SessionToken mismatch, expected: (sometoken), got (%s)", v.SessionToken)
|
|
}
|
|
}
|
|
|
|
// TestAWSConfig_shouldIAM is designed to test the scenario of running Terraform
|
|
// from an EC2 instance, without environment variables or manually supplied
|
|
// credentials.
|
|
func TestAWSConfig_shouldIgnoreIAM(t *testing.T) {
|
|
resetEnv := unsetEnv(t)
|
|
defer resetEnv()
|
|
// capture the test server's close method, to call after the test returns
|
|
ts := awsEnv(t)
|
|
defer ts()
|
|
simple := []struct {
|
|
Key, Secret, Token string
|
|
}{
|
|
{
|
|
Key: "test",
|
|
Secret: "secret",
|
|
}, {
|
|
Key: "test",
|
|
Secret: "test",
|
|
Token: "test",
|
|
},
|
|
}
|
|
|
|
for _, c := range simple {
|
|
cfg := Config{
|
|
AccessKey: c.Key,
|
|
SecretKey: c.Secret,
|
|
Token: c.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")
|
|
}
|
|
v, err := creds.Get()
|
|
if err != nil {
|
|
t.Fatalf("Error gettings creds: %s", err)
|
|
}
|
|
if v.AccessKeyID != c.Key {
|
|
t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID)
|
|
}
|
|
if v.SecretAccessKey != c.Secret {
|
|
t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey)
|
|
}
|
|
if v.SessionToken != c.Token {
|
|
t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken)
|
|
}
|
|
}
|
|
}
|
|
|
|
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, cfg.Profile, cfg.CredsFilename)
|
|
if creds == nil {
|
|
t.Fatalf("Expected a static creds provider to be returned")
|
|
}
|
|
v, err := creds.Get()
|
|
if err != nil {
|
|
t.Fatalf("Error gettings creds: %s", err)
|
|
}
|
|
if v.AccessKeyID != s {
|
|
t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", s, v.AccessKeyID)
|
|
}
|
|
if v.SecretAccessKey != s {
|
|
t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", s, v.SecretAccessKey)
|
|
}
|
|
if v.SessionToken != s {
|
|
t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", s, v.SessionToken)
|
|
}
|
|
}
|
|
|
|
// unsetEnv unsets enviornment variables for testing a "clean slate" with no
|
|
// credentials in the environment
|
|
func unsetEnv(t *testing.T) func() {
|
|
// Grab any existing AWS keys and preserve. In some tests we'll unset these, so
|
|
// we need to have them and restore them after
|
|
e := getEnv()
|
|
if err := os.Unsetenv("AWS_ACCESS_KEY_ID"); err != nil {
|
|
t.Fatalf("Error unsetting env var AWS_ACCESS_KEY_ID: %s", err)
|
|
}
|
|
if err := os.Unsetenv("AWS_SECRET_ACCESS_KEY"); err != nil {
|
|
t.Fatalf("Error unsetting env var AWS_SECRET_ACCESS_KEY: %s", err)
|
|
}
|
|
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
|
|
if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil {
|
|
t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err)
|
|
}
|
|
if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil {
|
|
t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err)
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
func setEnv(s string, t *testing.T) func() {
|
|
e := getEnv()
|
|
// Set all the envs to a dummy value
|
|
if err := os.Setenv("AWS_ACCESS_KEY_ID", s); err != nil {
|
|
t.Fatalf("Error setting env var AWS_ACCESS_KEY_ID: %s", err)
|
|
}
|
|
if err := os.Setenv("AWS_SECRET_ACCESS_KEY", s); err != nil {
|
|
t.Fatalf("Error setting env var AWS_SECRET_ACCESS_KEY: %s", err)
|
|
}
|
|
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
|
|
if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil {
|
|
t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err)
|
|
}
|
|
if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil {
|
|
t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err)
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// awsEnv establishes a httptest server to mock out the internal AWS Metadata
|
|
// service. IAM Credentials are retrieved by the EC2RoleProvider, which makes
|
|
// API calls to this internal URL. By replacing the server with a test server,
|
|
// we can simulate an AWS environment
|
|
func awsEnv(t *testing.T) func() {
|
|
routes := routes{}
|
|
if err := json.Unmarshal([]byte(aws_routes), &routes); err != nil {
|
|
t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err)
|
|
}
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
w.Header().Add("Server", "MockEC2")
|
|
for _, e := range routes.Endpoints {
|
|
if r.RequestURI == e.Uri {
|
|
fmt.Fprintln(w, e.Body)
|
|
}
|
|
}
|
|
}))
|
|
|
|
os.Setenv("AWS_METADATA_URL", ts.URL+"/latest")
|
|
return ts.Close
|
|
}
|
|
|
|
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"),
|
|
Profile: os.Getenv("AWS_TOKEN"),
|
|
CredsFilename: os.Getenv("AWS_SHARED_CREDENTIALS_FILE"),
|
|
}
|
|
}
|
|
|
|
// struct to preserve the current environment
|
|
type currentEnv struct {
|
|
Key, Secret, Token, Profile, CredsFilename string
|
|
}
|
|
|
|
type routes struct {
|
|
Endpoints []*endpoint `json:"endpoints"`
|
|
}
|
|
type endpoint struct {
|
|
Uri string `json:"uri"`
|
|
Body string `json:"body"`
|
|
}
|
|
|
|
const aws_routes = `
|
|
{
|
|
"endpoints": [
|
|
{
|
|
"uri": "/latest/meta-data/iam/security-credentials",
|
|
"body": "test_role"
|
|
},
|
|
{
|
|
"uri": "/latest/meta-data/iam/security-credentials/test_role",
|
|
"body": "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"somekey\",\"SecretAccessKey\":\"somesecret\",\"Token\":\"sometoken\"}"
|
|
}
|
|
]
|
|
}
|
|
`
|