diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index 85848bdc440c..e1cb51f3987d 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -227,143 +227,143 @@
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/awserr",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/client",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/defaults",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/request",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/session",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/restjson",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/restxml",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/apigateway",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/autoscaling",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudformation",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudfront",
@@ -372,148 +372,148 @@
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudtrail",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatch",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatchevents",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatchlogs",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/codecommit",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/codedeploy",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/directoryservice",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/dynamodb",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/ec2",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/ecr",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/ecs",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/efs",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/elasticache",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/elasticbeanstalk",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/elasticsearchservice",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/elb",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/firehose",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/glacier",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/iam",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/kinesis",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/kms",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/lambda",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/opsworks",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/rds",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/redshift",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/route53",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/s3",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/sns",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/sqs",
- "Comment": "v1.1.14",
- "Rev": "6876e9922ff299adf36e43e04c94820077968b3b"
+ "Comment": "v1.1.15",
+ "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
},
{
"ImportPath": "github.com/bgentry/speakeasy",
diff --git a/builtin/providers/aws/auth_helpers.go b/builtin/providers/aws/auth_helpers.go
new file mode 100644
index 000000000000..914c7e97174d
--- /dev/null
+++ b/builtin/providers/aws/auth_helpers.go
@@ -0,0 +1,134 @@
+package aws
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/aws/aws-sdk-go/aws/awserr"
+ awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
+ "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
+ "github.com/aws/aws-sdk-go/aws/ec2metadata"
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/aws/aws-sdk-go/service/iam"
+ "github.com/hashicorp/go-cleanhttp"
+)
+
+func GetAccountId(iamconn *iam.IAM, authProviderName string) (string, error) {
+ // If we have creds from instance profile, we can use metadata API
+ if authProviderName == ec2rolecreds.ProviderName {
+ log.Println("[DEBUG] Trying to get account ID via AWS Metadata API")
+
+ cfg := &aws.Config{}
+ setOptionalEndpoint(cfg)
+ metadataClient := ec2metadata.New(session.New(cfg))
+ info, err := metadataClient.IAMInfo()
+ if err != nil {
+ // This can be triggered when no IAM Role is assigned
+ // or AWS just happens to return invalid response
+ return "", fmt.Errorf("Failed getting EC2 IAM info: %s", err)
+ }
+
+ return parseAccountIdFromArn(info.InstanceProfileArn)
+ }
+
+ // Then try IAM GetUser
+ log.Println("[DEBUG] Trying to get account ID via iam:GetUser")
+ outUser, err := iamconn.GetUser(nil)
+ if err == nil {
+ return parseAccountIdFromArn(*outUser.User.Arn)
+ }
+
+ // Then try IAM ListRoles
+ awsErr, ok := err.(awserr.Error)
+ // AccessDenied and ValidationError can be raised
+ // if credentials belong to federated profile, so we ignore these
+ if !ok || (awsErr.Code() != "AccessDenied" && awsErr.Code() != "ValidationError") {
+ return "", fmt.Errorf("Failed getting account ID via 'iam:GetUser': %s", err)
+ }
+
+ log.Printf("[DEBUG] Getting account ID via iam:GetUser failed: %s", err)
+ log.Println("[DEBUG] Trying to get account ID via iam:ListRoles instead")
+ outRoles, err := iamconn.ListRoles(&iam.ListRolesInput{
+ MaxItems: aws.Int64(int64(1)),
+ })
+ if err != nil {
+ return "", fmt.Errorf("Failed getting account ID via 'iam:ListRoles': %s", err)
+ }
+
+ if len(outRoles.Roles) < 1 {
+ return "", fmt.Errorf("Failed getting account ID via 'iam:ListRoles': No roles available")
+ }
+
+ return parseAccountIdFromArn(*outRoles.Roles[0].Arn)
+}
+
+func parseAccountIdFromArn(arn string) (string, error) {
+ parts := strings.Split(arn, ":")
+ if len(parts) < 5 {
+ return "", fmt.Errorf("Unable to parse ID from invalid ARN: %q", arn)
+ }
+ return parts[4], nil
+}
+
+// This function is responsible for reading credentials from the
+// environment in the case that they're not explicitly specified
+// in the Terraform configuration.
+func GetCredentials(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{
+ AccessKeyID: key,
+ SecretAccessKey: secret,
+ SessionToken: token,
+ }},
+ &awsCredentials.EnvProvider{},
+ &awsCredentials.SharedCredentialsProvider{
+ Filename: credsfile,
+ Profile: profile,
+ },
+ }
+
+ // Build isolated HTTP client to avoid issues with globally-shared settings
+ client := cleanhttp.DefaultClient()
+
+ // Keep the timeout low as we don't want to wait in non-EC2 environments
+ client.Timeout = 100 * time.Millisecond
+ cfg := &aws.Config{
+ HTTPClient: client,
+ }
+ usedEndpoint := setOptionalEndpoint(cfg)
+
+ // Real AWS should reply to a simple metadata request.
+ // We check it actually does to ensure something else didn't just
+ // happen to be listening on the same IP:Port
+ metadataClient := ec2metadata.New(session.New(cfg))
+ if metadataClient.Available() {
+ providers = append(providers, &ec2rolecreds.EC2RoleProvider{
+ Client: metadataClient,
+ })
+ log.Printf("[INFO] AWS EC2 instance detected via default metadata" +
+ " API endpoint, EC2RoleProvider added to the auth chain")
+ } else {
+ if usedEndpoint == "" {
+ usedEndpoint = "default location"
+ }
+ log.Printf("[WARN] Ignoring AWS metadata API endpoint at %s "+
+ "as it doesn't return any instance-id", usedEndpoint)
+ }
+
+ return awsCredentials.NewChainCredentials(providers)
+}
+
+func setOptionalEndpoint(cfg *aws.Config) string {
+ endpoint := os.Getenv("AWS_METADATA_URL")
+ if endpoint != "" {
+ log.Printf("[INFO] Setting custom metadata endpoint: %q", endpoint)
+ cfg.Endpoint = aws.String(endpoint)
+ return endpoint
+ }
+ return ""
+}
diff --git a/builtin/providers/aws/auth_helpers_test.go b/builtin/providers/aws/auth_helpers_test.go
new file mode 100644
index 000000000000..b3f13403923e
--- /dev/null
+++ b/builtin/providers/aws/auth_helpers_test.go
@@ -0,0 +1,757 @@
+package aws
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/aws/aws-sdk-go/aws/awserr"
+ awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
+ "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/aws/aws-sdk-go/service/iam"
+)
+
+func TestAWSGetAccountId_shouldBeValid_fromEC2Role(t *testing.T) {
+ resetEnv := unsetEnv(t)
+ defer resetEnv()
+ // capture the test server's close method, to call after the test returns
+ awsTs := awsEnv(t)
+ defer awsTs()
+
+ iamEndpoints := []*iamEndpoint{}
+ ts, iamConn := getMockedAwsIamApi(iamEndpoints)
+ defer ts()
+
+ id, err := GetAccountId(iamConn, ec2rolecreds.ProviderName)
+ if err != nil {
+ t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err)
+ }
+
+ expectedAccountId := "123456789013"
+ if id != expectedAccountId {
+ t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
+ }
+}
+
+func TestAWSGetAccountId_shouldBeValid_EC2RoleHasPriority(t *testing.T) {
+ resetEnv := unsetEnv(t)
+ defer resetEnv()
+ // capture the test server's close method, to call after the test returns
+ awsTs := awsEnv(t)
+ defer awsTs()
+
+ iamEndpoints := []*iamEndpoint{
+ &iamEndpoint{
+ Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
+ Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"},
+ },
+ }
+ ts, iamConn := getMockedAwsIamApi(iamEndpoints)
+ defer ts()
+
+ id, err := GetAccountId(iamConn, ec2rolecreds.ProviderName)
+ if err != nil {
+ t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err)
+ }
+
+ expectedAccountId := "123456789013"
+ if id != expectedAccountId {
+ t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
+ }
+}
+
+func TestAWSGetAccountId_shouldBeValid_fromIamUser(t *testing.T) {
+ iamEndpoints := []*iamEndpoint{
+ &iamEndpoint{
+ Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
+ Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"},
+ },
+ }
+ ts, iamConn := getMockedAwsIamApi(iamEndpoints)
+ defer ts()
+
+ id, err := GetAccountId(iamConn, "")
+ if err != nil {
+ t.Fatalf("Getting account ID via GetUser failed: %s", err)
+ }
+
+ expectedAccountId := "123456789012"
+ if id != expectedAccountId {
+ t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
+ }
+}
+
+func TestAWSGetAccountId_shouldBeValid_fromIamListRoles(t *testing.T) {
+ iamEndpoints := []*iamEndpoint{
+ &iamEndpoint{
+ Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
+ Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
+ },
+ &iamEndpoint{
+ Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
+ Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"},
+ },
+ }
+ ts, iamConn := getMockedAwsIamApi(iamEndpoints)
+ defer ts()
+
+ id, err := GetAccountId(iamConn, "")
+ if err != nil {
+ t.Fatalf("Getting account ID via ListRoles failed: %s", err)
+ }
+
+ expectedAccountId := "123456789012"
+ if id != expectedAccountId {
+ t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
+ }
+}
+
+func TestAWSGetAccountId_shouldBeValid_federatedRole(t *testing.T) {
+ iamEndpoints := []*iamEndpoint{
+ &iamEndpoint{
+ Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
+ Response: &iamResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"},
+ },
+ &iamEndpoint{
+ Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
+ Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"},
+ },
+ }
+ ts, iamConn := getMockedAwsIamApi(iamEndpoints)
+ defer ts()
+
+ id, err := GetAccountId(iamConn, "")
+ if err != nil {
+ t.Fatalf("Getting account ID via ListRoles failed: %s", err)
+ }
+
+ expectedAccountId := "123456789012"
+ if id != expectedAccountId {
+ t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
+ }
+}
+
+func TestAWSGetAccountId_shouldError_unauthorizedFromIam(t *testing.T) {
+ iamEndpoints := []*iamEndpoint{
+ &iamEndpoint{
+ Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
+ Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
+ },
+ &iamEndpoint{
+ Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
+ Response: &iamResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"},
+ },
+ }
+ ts, iamConn := getMockedAwsIamApi(iamEndpoints)
+ defer ts()
+
+ id, err := GetAccountId(iamConn, "")
+ if err == nil {
+ t.Fatal("Expected error when getting account ID")
+ }
+
+ if id != "" {
+ t.Fatalf("Expected no account ID, given: %s", id)
+ }
+}
+
+func TestAWSParseAccountIdFromArn(t *testing.T) {
+ validArn := "arn:aws:iam::101636750127:instance-profile/aws-elasticbeanstalk-ec2-role"
+ expectedId := "101636750127"
+ id, err := parseAccountIdFromArn(validArn)
+ if err != nil {
+ t.Fatalf("Expected no error when parsing valid ARN: %s", err)
+ }
+ if id != expectedId {
+ t.Fatalf("Parsed id doesn't match with expected (%q != %q)", id, expectedId)
+ }
+
+ invalidArn := "blablah"
+ id, err = parseAccountIdFromArn(invalidArn)
+ if err == nil {
+ t.Fatalf("Expected error when parsing invalid ARN (%q)", invalidArn)
+ }
+}
+
+func TestAWSGetCredentials_shouldError(t *testing.T) {
+ resetEnv := unsetEnv(t)
+ defer resetEnv()
+ cfg := Config{}
+
+ c := GetCredentials(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 TestAWSGetCredentials_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 := GetCredentials(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)
+ }
+ }
+}
+
+// TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform
+// from an EC2 instance, without environment variables or manually supplied
+// credentials.
+func TestAWSGetCredentials_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 := GetCredentials(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)
+ }
+}
+
+// TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform
+// from an EC2 instance, without environment variables or manually supplied
+// credentials.
+func TestAWSGetCredentials_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 := GetCredentials(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)
+ }
+ }
+}
+
+func TestAWSGetCredentials_shouldErrorWithInvalidEndpoint(t *testing.T) {
+ resetEnv := unsetEnv(t)
+ defer resetEnv()
+ // capture the test server's close method, to call after the test returns
+ ts := invalidAwsEnv(t)
+ defer ts()
+
+ creds := GetCredentials("", "", "", "", "")
+ v, err := creds.Get()
+ if err == nil {
+ t.Fatal("Expected error returned when getting creds w/ invalid EC2 endpoint")
+ }
+
+ if v.ProviderName != "" {
+ t.Fatalf("Expected provider name to be empty, %q given", v.ProviderName)
+ }
+}
+
+func TestAWSGetCredentials_shouldIgnoreInvalidEndpoint(t *testing.T) {
+ resetEnv := unsetEnv(t)
+ defer resetEnv()
+ // capture the test server's close method, to call after the test returns
+ ts := invalidAwsEnv(t)
+ defer ts()
+
+ creds := GetCredentials("accessKey", "secretKey", "", "", "")
+ v, err := creds.Get()
+ if err != nil {
+ t.Fatalf("Getting static credentials w/ invalid EC2 endpoint failed: %s", err)
+ }
+
+ if v.ProviderName != "StaticProvider" {
+ t.Fatalf("Expected provider name to be %q, %q given", "StaticProvider", v.ProviderName)
+ }
+
+ if v.AccessKeyID != "accessKey" {
+ t.Fatalf("Static Access Key %q doesn't match: %s", "accessKey", v.AccessKeyID)
+ }
+
+ if v.SecretAccessKey != "secretKey" {
+ t.Fatalf("Static Secret Key %q doesn't match: %s", "secretKey", v.SecretAccessKey)
+ }
+}
+
+func TestAWSGetCredentials_shouldCatchEC2RoleProvider(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()
+
+ creds := GetCredentials("", "", "", "", "")
+ if creds == nil {
+ t.Fatalf("Expected an EC2Role creds provider to be returned")
+ }
+ v, err := creds.Get()
+ if err != nil {
+ t.Fatalf("Expected no error when getting creds: %s", err)
+ }
+ expectedProvider := "EC2RoleProvider"
+ if v.ProviderName != expectedProvider {
+ t.Fatalf("Expected provider name to be %q, %q given",
+ expectedProvider, v.ProviderName)
+ }
+}
+
+var credentialsFileContents = `[myprofile]
+aws_access_key_id = accesskey
+aws_secret_access_key = secretkey
+`
+
+func TestAWSGetCredentials_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 := GetCredentials("", "", "", "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 TestAWSGetCredentials_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 := GetCredentials(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(metadataApiRoutes), &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")
+ log.Printf("[DEBUG] Mocker server received request to %q", r.RequestURI)
+ for _, e := range routes.Endpoints {
+ if r.RequestURI == e.Uri {
+ fmt.Fprintln(w, e.Body)
+ w.WriteHeader(200)
+ return
+ }
+ }
+ w.WriteHeader(400)
+ }))
+
+ os.Setenv("AWS_METADATA_URL", ts.URL+"/latest")
+ return ts.Close
+}
+
+// invalidAwsEnv establishes a httptest server to simulate behaviour
+// when endpoint doesn't respond as expected
+func invalidAwsEnv(t *testing.T) func() {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(400)
+ }))
+
+ os.Setenv("AWS_METADATA_URL", ts.URL+"/latest")
+ return ts.Close
+}
+
+// getMockedAwsIamApi establishes a httptest server to simulate behaviour
+// of a real AWS' IAM server
+func getMockedAwsIamApi(endpoints []*iamEndpoint) (func(), *iam.IAM) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ buf := new(bytes.Buffer)
+ buf.ReadFrom(r.Body)
+ requestBody := buf.String()
+
+ log.Printf("[DEBUG] Received IAM API %q request to %q: %s",
+ r.Method, r.RequestURI, requestBody)
+
+ for _, e := range endpoints {
+ if r.Method == e.Request.Method && r.RequestURI == e.Request.Uri && requestBody == e.Request.Body {
+ log.Printf("[DEBUG] Mock API responding with %d: %s", e.Response.StatusCode, e.Response.Body)
+
+ w.WriteHeader(e.Response.StatusCode)
+ w.Header().Set("Content-Type", e.Response.ContentType)
+ w.Header().Set("X-Amzn-Requestid", "1b206dd1-f9a8-11e5-becf-051c60f11c4a")
+ w.Header().Set("Date", time.Now().Format(time.RFC1123))
+
+ fmt.Fprintln(w, e.Response.Body)
+ return
+ }
+ }
+
+ w.WriteHeader(400)
+ return
+ }))
+
+ sc := awsCredentials.NewStaticCredentials("accessKey", "secretKey", "")
+
+ sess := session.New(&aws.Config{
+ Credentials: sc,
+ Region: aws.String("us-east-1"),
+ Endpoint: aws.String(ts.URL),
+ CredentialsChainVerboseErrors: aws.Bool(true),
+ })
+ iamConn := iam.New(sess)
+
+ return ts.Close, iamConn
+}
+
+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 metadataApiRoutes = `
+{
+ "endpoints": [
+ {
+ "uri": "/latest/meta-data/instance-id",
+ "body": "mock-instance-id"
+ },
+ {
+ "uri": "/latest/meta-data/iam/info",
+ "body": "{\"Code\": \"Success\",\"LastUpdated\": \"2016-03-17T12:27:32Z\",\"InstanceProfileArn\": \"arn:aws:iam::123456789013:instance-profile/my-instance-profile\",\"InstanceProfileId\": \"AIPAABCDEFGHIJKLMN123\"}"
+ },
+ {
+ "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\"}"
+ }
+ ]
+}
+`
+
+type iamEndpoint struct {
+ Request *iamRequest
+ Response *iamResponse
+}
+
+type iamRequest struct {
+ Method string
+ Uri string
+ Body string
+}
+
+type iamResponse struct {
+ StatusCode int
+ Body string
+ ContentType string
+}
+
+const iamResponse_GetUser_valid = `
+
+
+ AIDACKCEVSQ6C2EXAMPLE
+ /division_abc/subdivision_xyz/
+ Bob
+ arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob
+ 2013-10-02T17:01:44Z
+ 2014-10-10T14:37:51Z
+
+
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+
+`
+
+const iamResponse_GetUser_unauthorized = `
+
+ Sender
+ AccessDenied
+ User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:GetUser on resource: arn:aws:iam::123456789012:user/Bob
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+`
+
+const iamResponse_GetUser_federatedFailure = `
+
+ Sender
+ ValidationError
+ Must specify userName when calling with non-User credentials
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+`
+
+const iamResponse_ListRoles_valid = `
+
+ true
+ AWceSSsKsazQ4IEplT9o4hURCzBs00iavlEvEXAMPLE
+
+
+ /
+ %7B%22Version%22%3A%222008-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22ec2.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D
+ AROACKCEVSQ6C2EXAMPLE
+ elasticbeanstalk-role
+ arn:aws:iam::123456789012:role/elasticbeanstalk-role
+ 2013-10-02T17:01:44Z
+
+
+
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+
+`
+
+const iamResponse_ListRoles_unauthorized = `
+
+ Sender
+ AccessDenied
+ User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:ListRoles on resource: arn:aws:iam::123456789012:role/
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+`
diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go
index f521c755a13a..82a82e016f49 100644
--- a/builtin/providers/aws/config.go
+++ b/builtin/providers/aws/config.go
@@ -4,9 +4,7 @@ import (
"fmt"
"log"
"net/http"
- "os"
"strings"
- "time"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/go-multierror"
@@ -17,9 +15,6 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
- awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
- "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
- "github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/apigateway"
@@ -133,10 +128,10 @@ 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, c.Profile, c.CredsFilename)
+ creds := GetCredentials(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()
+ cp, err := creds.Get()
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
errs = append(errs, fmt.Errorf(`No valid credential sources found for AWS Provider.
@@ -147,6 +142,9 @@ func (c *Config) Client() (interface{}, error) {
}
return nil, &multierror.Error{Errors: errs}
}
+
+ log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
+
awsConfig := &aws.Config{
Credentials: creds,
Region: aws.String(c.Region),
@@ -177,6 +175,7 @@ func (c *Config) Client() (interface{}, error) {
err = c.ValidateCredentials(client.iamconn)
if err != nil {
errs = append(errs, err)
+ return nil, &multierror.Error{Errors: errs}
}
// Some services exist only in us-east-1, e.g. because they manage
@@ -216,7 +215,7 @@ func (c *Config) Client() (interface{}, error) {
log.Println("[INFO] Initializing Elastic Beanstalk Connection")
client.elasticbeanstalkconn = elasticbeanstalk.New(sess)
- authErr := c.ValidateAccountId(client.iamconn)
+ authErr := c.ValidateAccountId(client.iamconn, cp.ProviderName)
if authErr != nil {
errs = append(errs, authErr)
}
@@ -323,7 +322,7 @@ func (c *Config) ValidateCredentials(iamconn *iam.IAM) error {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "AccessDenied" || awsErr.Code() == "ValidationError" {
- log.Printf("[WARN] AccessDenied Error with iam.GetUser, assuming IAM profile")
+ log.Printf("[WARN] AccessDenied Error with iam.GetUser, assuming IAM role")
// User may be an IAM instance profile, or otherwise IAM role without the
// GetUser permissions, so fail silently
return nil
@@ -339,31 +338,17 @@ func (c *Config) ValidateCredentials(iamconn *iam.IAM) error {
// ValidateAccountId returns a context-specific error if the configured account
// id is explicitly forbidden or not authorised; and nil if it is authorised.
-func (c *Config) ValidateAccountId(iamconn *iam.IAM) error {
+func (c *Config) ValidateAccountId(iamconn *iam.IAM, authProviderName string) error {
if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil {
return nil
}
log.Printf("[INFO] Validating account ID")
-
- out, err := iamconn.GetUser(nil)
-
+ account_id, err := GetAccountId(iamconn, authProviderName)
if err != nil {
- awsErr, _ := err.(awserr.Error)
- if awsErr.Code() == "ValidationError" {
- log.Printf("[WARN] ValidationError with iam.GetUser, assuming its an IAM profile")
- // User may be an IAM instance profile, so fail silently.
- // If it is an IAM instance profile
- // validating account might be superfluous
- return nil
- } else {
- return fmt.Errorf("Failed getting account ID from IAM: %s", err)
- // return error if the account id is explicitly not authorised
- }
+ return err
}
- account_id := strings.Split(*out.User.Arn, ":")[4]
-
if c.ForbiddenAccountIds != nil {
for _, id := range c.ForbiddenAccountIds {
if id == account_id {
@@ -384,59 +369,6 @@ func (c *Config) ValidateAccountId(iamconn *iam.IAM) error {
return nil
}
-// 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, profile, credsfile string) *awsCredentials.Credentials {
- // build a chain provider, lazy-evaulated by aws-sdk
- providers := []awsCredentials.Provider{
- &awsCredentials.StaticProvider{Value: awsCredentials.Value{
- AccessKeyID: key,
- SecretAccessKey: secret,
- SessionToken: token,
- }},
- &awsCredentials.EnvProvider{},
- &awsCredentials.SharedCredentialsProvider{
- Filename: credsfile,
- Profile: profile,
- },
- }
-
- // We only look in the EC2 metadata API if we can connect
- // to the metadata service within a reasonable amount of time
- metadataURL := os.Getenv("AWS_METADATA_URL")
- if metadataURL == "" {
- metadataURL = "http://169.254.169.254:80/latest"
- }
- c := http.Client{
- Timeout: 100 * time.Millisecond,
- }
-
- r, err := c.Get(metadataURL)
- // Flag to determine if we should add the EC2Meta data provider. Default false
- var useIAM bool
- if err == nil {
- // AWS will add a "Server: EC2ws" header value for the metadata request. We
- // check the headers for this value to ensure something else didn't just
- // happent to be listening on that IP:Port
- if r.Header["Server"] != nil && strings.Contains(r.Header["Server"][0], "EC2") {
- useIAM = true
- }
- }
-
- if useIAM {
- log.Printf("[DEBUG] EC2 Metadata service found, adding EC2 Role Credential Provider")
- providers = append(providers, &ec2rolecreds.EC2RoleProvider{
- Client: ec2metadata.New(session.New(&aws.Config{
- Endpoint: aws.String(metadataURL),
- })),
- })
- } else {
- log.Printf("[DEBUG] EC2 Metadata service not found, not adding EC2 Role Credential Provider")
- }
- return awsCredentials.NewChainCredentials(providers)
-}
-
// addTerraformVersionToUserAgent is a named handler that will add Terraform's
// version information to requests made by the AWS SDK.
var addTerraformVersionToUserAgent = request.NamedHandler{
diff --git a/builtin/providers/aws/config_test.go b/builtin/providers/aws/config_test.go
deleted file mode 100644
index 5c58a57290fb..000000000000
--- a/builtin/providers/aws/config_test.go
+++ /dev/null
@@ -1,376 +0,0 @@
-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\"}"
- }
- ]
-}
-`
diff --git a/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go b/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go
index 83badef7cc81..f664caf0946e 100644
--- a/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go
+++ b/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go
@@ -32,19 +32,37 @@ func (d DefaultRetryer) MaxRetries() int {
// RetryRules returns the delay duration before retrying this request again
func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
// Set the upper limit of delay in retrying at ~five minutes
+ minTime := 30
+ throttle := d.shouldThrottle(r)
+ if throttle {
+ minTime = 1000
+ }
+
retryCount := r.RetryCount
if retryCount > 13 {
retryCount = 13
+ } else if throttle && retryCount > 8 {
+ retryCount = 8
}
- delay := (1 << uint(retryCount)) * (rand.Intn(30) + 30)
+ delay := (1 << uint(retryCount)) * (rand.Intn(30) + minTime)
return time.Duration(delay) * time.Millisecond
}
-// ShouldRetry returns if the request should be retried.
+// ShouldRetry returns true if the request should be retried.
func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
if r.HTTPResponse.StatusCode >= 500 {
return true
}
- return r.IsErrorRetryable()
+ return r.IsErrorRetryable() || d.shouldThrottle(r)
+}
+
+// ShouldThrottle returns true if the request should be throttled.
+func (d DefaultRetryer) shouldThrottle(r *request.Request) bool {
+ if r.HTTPResponse.StatusCode == 502 ||
+ r.HTTPResponse.StatusCode == 503 ||
+ r.HTTPResponse.StatusCode == 504 {
+ return true
+ }
+ return r.IsErrorThrottle()
}
diff --git a/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go
index 8edcfc926bd9..669c813a00d5 100644
--- a/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go
+++ b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go
@@ -2,6 +2,7 @@ package ec2metadata
import (
"encoding/json"
+ "fmt"
"path"
"strings"
"time"
@@ -49,7 +50,7 @@ func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument
resp, err := c.GetDynamicData("instance-identity/document")
if err != nil {
return EC2InstanceIdentityDocument{},
- awserr.New("EC2RoleRequestError",
+ awserr.New("EC2MetadataRequestError",
"failed to get EC2 instance identity document", err)
}
@@ -63,6 +64,31 @@ func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument
return doc, nil
}
+// IAMInfo retrieves IAM info from the metadata API
+func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) {
+ resp, err := c.GetMetadata("iam/info")
+ if err != nil {
+ return EC2IAMInfo{},
+ awserr.New("EC2MetadataRequestError",
+ "failed to get EC2 IAM info", err)
+ }
+
+ info := EC2IAMInfo{}
+ if err := json.NewDecoder(strings.NewReader(resp)).Decode(&info); err != nil {
+ return EC2IAMInfo{},
+ awserr.New("SerializationError",
+ "failed to decode EC2 IAM info", err)
+ }
+
+ if info.Code != "Success" {
+ errMsg := fmt.Sprintf("failed to get EC2 IAM Info (%s)", info.Code)
+ return EC2IAMInfo{},
+ awserr.New("EC2MetadataError", errMsg, nil)
+ }
+
+ return info, nil
+}
+
// Region returns the region the instance is running in.
func (c *EC2Metadata) Region() (string, error) {
resp, err := c.GetMetadata("placement/availability-zone")
@@ -85,6 +111,15 @@ func (c *EC2Metadata) Available() bool {
return true
}
+// An EC2IAMInfo provides the shape for unmarshalling
+// an IAM info from the metadata API
+type EC2IAMInfo struct {
+ Code string
+ LastUpdated time.Time
+ InstanceProfileArn string
+ InstanceProfileID string
+}
+
// An EC2InstanceIdentityDocument provides the shape for unmarshalling
// an instance identity document
type EC2InstanceIdentityDocument struct {
diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go b/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go
index ab6fff5ac842..8cc8b015ae6c 100644
--- a/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go
+++ b/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go
@@ -26,8 +26,11 @@ func WithRetryer(cfg *aws.Config, retryer Retryer) *aws.Config {
// retryableCodes is a collection of service response codes which are retry-able
// without any further action.
var retryableCodes = map[string]struct{}{
- "RequestError": {},
- "RequestTimeout": {},
+ "RequestError": {},
+ "RequestTimeout": {},
+}
+
+var throttleCodes = map[string]struct{}{
"ProvisionedThroughputExceededException": {},
"Throttling": {},
"ThrottlingException": {},
@@ -46,6 +49,11 @@ var credsExpiredCodes = map[string]struct{}{
"RequestExpired": {}, // EC2 Only
}
+func isCodeThrottle(code string) bool {
+ _, ok := throttleCodes[code]
+ return ok
+}
+
func isCodeRetryable(code string) bool {
if _, ok := retryableCodes[code]; ok {
return true
@@ -70,6 +78,17 @@ func (r *Request) IsErrorRetryable() bool {
return false
}
+// IsErrorThrottle returns whether the error is to be throttled based on its code.
+// Returns false if the request has no Error set
+func (r *Request) IsErrorThrottle() bool {
+ if r.Error != nil {
+ if err, ok := r.Error.(awserr.Error); ok {
+ return isCodeThrottle(err.Code())
+ }
+ }
+ return false
+}
+
// IsErrorExpired returns whether the error code is a credential expiry error.
// Returns false if the request has no Error set.
func (r *Request) IsErrorExpired() bool {
diff --git a/vendor/github.com/aws/aws-sdk-go/aws/types.go b/vendor/github.com/aws/aws-sdk-go/aws/types.go
index 0f067c57f4e2..fa014b49e1d7 100644
--- a/vendor/github.com/aws/aws-sdk-go/aws/types.go
+++ b/vendor/github.com/aws/aws-sdk-go/aws/types.go
@@ -61,23 +61,41 @@ func (r ReaderSeekerCloser) Close() error {
type WriteAtBuffer struct {
buf []byte
m sync.Mutex
+
+ // GrowthCoeff defines the growth rate of the internal buffer. By
+ // default, the growth rate is 1, where expanding the internal
+ // buffer will allocate only enough capacity to fit the new expected
+ // length.
+ GrowthCoeff float64
+}
+
+// NewWriteAtBuffer creates a WriteAtBuffer with an internal buffer
+// provided by buf.
+func NewWriteAtBuffer(buf []byte) *WriteAtBuffer {
+ return &WriteAtBuffer{buf: buf}
}
// WriteAt writes a slice of bytes to a buffer starting at the position provided
// The number of bytes written will be returned, or error. Can overwrite previous
// written slices if the write ats overlap.
func (b *WriteAtBuffer) WriteAt(p []byte, pos int64) (n int, err error) {
+ pLen := len(p)
+ expLen := pos + int64(pLen)
b.m.Lock()
defer b.m.Unlock()
-
- expLen := pos + int64(len(p))
if int64(len(b.buf)) < expLen {
- newBuf := make([]byte, expLen)
- copy(newBuf, b.buf)
- b.buf = newBuf
+ if int64(cap(b.buf)) < expLen {
+ if b.GrowthCoeff < 1 {
+ b.GrowthCoeff = 1
+ }
+ newBuf := make([]byte, expLen, int64(b.GrowthCoeff*float64(expLen)))
+ copy(newBuf, b.buf)
+ b.buf = newBuf
+ }
+ b.buf = b.buf[:expLen]
}
copy(b.buf[pos:], p)
- return len(p), nil
+ return pLen, nil
}
// Bytes returns a slice of bytes written to the buffer.
diff --git a/vendor/github.com/aws/aws-sdk-go/aws/version.go b/vendor/github.com/aws/aws-sdk-go/aws/version.go
index 458620f88b4e..805dc711dbb0 100644
--- a/vendor/github.com/aws/aws-sdk-go/aws/version.go
+++ b/vendor/github.com/aws/aws-sdk-go/aws/version.go
@@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK
-const SDKVersion = "1.1.14"
+const SDKVersion = "1.1.15"
diff --git a/vendor/github.com/aws/aws-sdk-go/service/autoscaling/waiters.go b/vendor/github.com/aws/aws-sdk-go/service/autoscaling/waiters.go
index 42ffccb852dc..42595d2178b0 100644
--- a/vendor/github.com/aws/aws-sdk-go/service/autoscaling/waiters.go
+++ b/vendor/github.com/aws/aws-sdk-go/service/autoscaling/waiters.go
@@ -14,15 +14,15 @@ func (c *AutoScaling) WaitUntilGroupExists(input *DescribeAutoScalingGroupsInput
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
- Matcher: "pathAll",
- Argument: "length(AutoScalingGroups)",
- Expected: 1,
+ Matcher: "path",
+ Argument: "length(AutoScalingGroups) > `0`",
+ Expected: true,
},
{
State: "retry",
- Matcher: "pathAll",
- Argument: "length(AutoScalingGroups)",
- Expected: 0,
+ Matcher: "path",
+ Argument: "length(AutoScalingGroups) > `0`",
+ Expected: false,
},
},
}
@@ -43,13 +43,13 @@ func (c *AutoScaling) WaitUntilGroupInService(input *DescribeAutoScalingGroupsIn
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
- Matcher: "pathAll",
+ Matcher: "path",
Argument: "contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)",
Expected: false,
},
{
State: "retry",
- Matcher: "pathAll",
+ Matcher: "path",
Argument: "contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)",
Expected: true,
},
@@ -72,15 +72,15 @@ func (c *AutoScaling) WaitUntilGroupNotExists(input *DescribeAutoScalingGroupsIn
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
- Matcher: "pathAll",
- Argument: "length(AutoScalingGroups)",
- Expected: 0,
+ Matcher: "path",
+ Argument: "length(AutoScalingGroups) > `0`",
+ Expected: false,
},
{
State: "retry",
- Matcher: "pathAll",
- Argument: "length(AutoScalingGroups)",
- Expected: 1,
+ Matcher: "path",
+ Argument: "length(AutoScalingGroups) > `0`",
+ Expected: true,
},
},
}
diff --git a/vendor/github.com/aws/aws-sdk-go/service/cloudformation/api.go b/vendor/github.com/aws/aws-sdk-go/service/cloudformation/api.go
index ca79c979cfa2..7ace19458c8a 100644
--- a/vendor/github.com/aws/aws-sdk-go/service/cloudformation/api.go
+++ b/vendor/github.com/aws/aws-sdk-go/service/cloudformation/api.go
@@ -83,6 +83,46 @@ func (c *CloudFormation) ContinueUpdateRollback(input *ContinueUpdateRollbackInp
return out, err
}
+const opCreateChangeSet = "CreateChangeSet"
+
+// CreateChangeSetRequest generates a request for the CreateChangeSet operation.
+func (c *CloudFormation) CreateChangeSetRequest(input *CreateChangeSetInput) (req *request.Request, output *CreateChangeSetOutput) {
+ op := &request.Operation{
+ Name: opCreateChangeSet,
+ HTTPMethod: "POST",
+ HTTPPath: "/",
+ }
+
+ if input == nil {
+ input = &CreateChangeSetInput{}
+ }
+
+ req = c.newRequest(op, input, output)
+ output = &CreateChangeSetOutput{}
+ req.Data = output
+ return
+}
+
+// Creates a list of changes for a stack. AWS CloudFormation generates the change
+// set by comparing the stack's information with the information that you submit.
+// A change set can help you understand which resources AWS CloudFormation will
+// change and how it will change them before you update your stack. Change sets
+// allow you to check before you make a change so that you don't delete or replace
+// critical resources.
+//
+// AWS CloudFormation doesn't make any changes to the stack when you create
+// a change set. To make the specified changes, you must execute the change
+// set by using the ExecuteChangeSet action.
+//
+// After the call successfully completes, AWS CloudFormation starts creating
+// the change set. To check the status of the change set, use the DescribeChangeSet
+// action.
+func (c *CloudFormation) CreateChangeSet(input *CreateChangeSetInput) (*CreateChangeSetOutput, error) {
+ req, out := c.CreateChangeSetRequest(input)
+ err := req.Send()
+ return out, err
+}
+
const opCreateStack = "CreateStack"
// CreateStackRequest generates a request for the CreateStack operation.
@@ -112,6 +152,37 @@ func (c *CloudFormation) CreateStack(input *CreateStackInput) (*CreateStackOutpu
return out, err
}
+const opDeleteChangeSet = "DeleteChangeSet"
+
+// DeleteChangeSetRequest generates a request for the DeleteChangeSet operation.
+func (c *CloudFormation) DeleteChangeSetRequest(input *DeleteChangeSetInput) (req *request.Request, output *DeleteChangeSetOutput) {
+ op := &request.Operation{
+ Name: opDeleteChangeSet,
+ HTTPMethod: "POST",
+ HTTPPath: "/",
+ }
+
+ if input == nil {
+ input = &DeleteChangeSetInput{}
+ }
+
+ req = c.newRequest(op, input, output)
+ output = &DeleteChangeSetOutput{}
+ req.Data = output
+ return
+}
+
+// Deletes the specified change set. Deleting change sets ensures that no one
+// executes the wrong change set.
+//
+// If the call successfully completes, AWS CloudFormation successfully deleted
+// the change set.
+func (c *CloudFormation) DeleteChangeSet(input *DeleteChangeSetInput) (*DeleteChangeSetOutput, error) {
+ req, out := c.DeleteChangeSetRequest(input)
+ err := req.Send()
+ return out, err
+}
+
const opDeleteStack = "DeleteStack"
// DeleteStackRequest generates a request for the DeleteStack operation.
@@ -171,6 +242,36 @@ func (c *CloudFormation) DescribeAccountLimits(input *DescribeAccountLimitsInput
return out, err
}
+const opDescribeChangeSet = "DescribeChangeSet"
+
+// DescribeChangeSetRequest generates a request for the DescribeChangeSet operation.
+func (c *CloudFormation) DescribeChangeSetRequest(input *DescribeChangeSetInput) (req *request.Request, output *DescribeChangeSetOutput) {
+ op := &request.Operation{
+ Name: opDescribeChangeSet,
+ HTTPMethod: "POST",
+ HTTPPath: "/",
+ }
+
+ if input == nil {
+ input = &DescribeChangeSetInput{}
+ }
+
+ req = c.newRequest(op, input, output)
+ output = &DescribeChangeSetOutput{}
+ req.Data = output
+ return
+}
+
+// Returns the inputs for the change set and a list of changes that AWS CloudFormation
+// will make if you execute the change set. For more information, see Updating
+// Stacks Using Change Sets (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html)
+// in the AWS CloudFormation User Guide.
+func (c *CloudFormation) DescribeChangeSet(input *DescribeChangeSetInput) (*DescribeChangeSetOutput, error) {
+ req, out := c.DescribeChangeSetRequest(input)
+ err := req.Send()
+ return out, err
+}
+
const opDescribeStackEvents = "DescribeStackEvents"
// DescribeStackEventsRequest generates a request for the DescribeStackEvents operation.
@@ -361,6 +462,44 @@ func (c *CloudFormation) EstimateTemplateCost(input *EstimateTemplateCostInput)
return out, err
}
+const opExecuteChangeSet = "ExecuteChangeSet"
+
+// ExecuteChangeSetRequest generates a request for the ExecuteChangeSet operation.
+func (c *CloudFormation) ExecuteChangeSetRequest(input *ExecuteChangeSetInput) (req *request.Request, output *ExecuteChangeSetOutput) {
+ op := &request.Operation{
+ Name: opExecuteChangeSet,
+ HTTPMethod: "POST",
+ HTTPPath: "/",
+ }
+
+ if input == nil {
+ input = &ExecuteChangeSetInput{}
+ }
+
+ req = c.newRequest(op, input, output)
+ output = &ExecuteChangeSetOutput{}
+ req.Data = output
+ return
+}
+
+// Updates a stack using the input information that was provided when the specified
+// change set was created. After the call successfully completes, AWS CloudFormation
+// starts updating the stack. Use the DescribeStacks action to view the status
+// of the update.
+//
+// When you execute a change set, AWS CloudFormation deletes all other change
+// sets associated with the stack because they aren't valid for the updated
+// stack.
+//
+// If a stack policy is associated with the stack, AWS CloudFormation enforces
+// the policy during the update. You can't specify a temporary stack policy
+// that overrides the current policy.
+func (c *CloudFormation) ExecuteChangeSet(input *ExecuteChangeSetInput) (*ExecuteChangeSetOutput, error) {
+ req, out := c.ExecuteChangeSetRequest(input)
+ err := req.Send()
+ return out, err
+}
+
const opGetStackPolicy = "GetStackPolicy"
// GetStackPolicyRequest generates a request for the GetStackPolicy operation.
@@ -458,6 +597,35 @@ func (c *CloudFormation) GetTemplateSummary(input *GetTemplateSummaryInput) (*Ge
return out, err
}
+const opListChangeSets = "ListChangeSets"
+
+// ListChangeSetsRequest generates a request for the ListChangeSets operation.
+func (c *CloudFormation) ListChangeSetsRequest(input *ListChangeSetsInput) (req *request.Request, output *ListChangeSetsOutput) {
+ op := &request.Operation{
+ Name: opListChangeSets,
+ HTTPMethod: "POST",
+ HTTPPath: "/",
+ }
+
+ if input == nil {
+ input = &ListChangeSetsInput{}
+ }
+
+ req = c.newRequest(op, input, output)
+ output = &ListChangeSetsOutput{}
+ req.Data = output
+ return
+}
+
+// Returns the ID and status of each active change set for a stack. For example,
+// AWS CloudFormation lists change sets that are in the CREATE_IN_PROGRESS or
+// CREATE_PENDING state.
+func (c *CloudFormation) ListChangeSets(input *ListChangeSetsInput) (*ListChangeSetsOutput, error) {
+ req, out := c.ListChangeSetsRequest(input)
+ err := req.Send()
+ return out, err
+}
+
const opListStackResources = "ListStackResources"
// ListStackResourcesRequest generates a request for the ListStackResources operation.
@@ -725,6 +893,72 @@ func (s CancelUpdateStackOutput) GoString() string {
return s.String()
}
+// The Change structure describes the changes AWS CloudFormation will perform
+// if you execute the change set.
+type Change struct {
+ _ struct{} `type:"structure"`
+
+ // A ResourceChange structure that describes the resource and action that AWS
+ // CloudFormation will perform.
+ ResourceChange *ResourceChange `type:"structure"`
+
+ // The type of entity that AWS CloudFormation changes. Currently, the only entity
+ // type is Resource.
+ Type *string `type:"string" enum:"ChangeType"`
+}
+
+// String returns the string representation
+func (s Change) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s Change) GoString() string {
+ return s.String()
+}
+
+// The ChangeSetSummary structure describes a change set, its status, and the
+// stack with which it's associated.
+type ChangeSetSummary struct {
+ _ struct{} `type:"structure"`
+
+ // The ID of the change set.
+ ChangeSetId *string `min:"1" type:"string"`
+
+ // The name of the change set.
+ ChangeSetName *string `min:"1" type:"string"`
+
+ // The start time when the change set was created, in UTC.
+ CreationTime *time.Time `type:"timestamp" timestampFormat:"iso8601"`
+
+ // Descriptive information about the change set.
+ Description *string `min:"1" type:"string"`
+
+ // The ID of the stack with which the change set is associated.
+ StackId *string `type:"string"`
+
+ // The name of the stack with which the change set is associated.
+ StackName *string `type:"string"`
+
+ // The state of the change set, such as CREATE_IN_PROGRESS, CREATE_COMPLETE,
+ // or FAILED.
+ Status *string `type:"string" enum:"ChangeSetStatus"`
+
+ // A description of the change set's status. For example, if your change set
+ // is in the FAILED state, AWS CloudFormation shows the error message.
+ StatusReason *string `type:"string"`
+}
+
+// String returns the string representation
+func (s ChangeSetSummary) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ChangeSetSummary) GoString() string {
+ return s.String()
+}
+
// The input for the ContinueUpdateRollback action.
type ContinueUpdateRollbackInput struct {
_ struct{} `type:"structure"`
@@ -759,13 +993,137 @@ func (s ContinueUpdateRollbackOutput) GoString() string {
return s.String()
}
+// The input for the CreateChangeSet action.
+type CreateChangeSetInput struct {
+ _ struct{} `type:"structure"`
+
+ // A list of capabilities that you must specify before AWS CloudFormation can
+ // update certain stacks. Some stack templates might include resources that
+ // can affect permissions in your AWS account, for example, by creating new
+ // AWS Identity and Access Management (IAM) users. For those stacks, you must
+ // explicitly acknowledge their capabilities by specifying this parameter.
+ //
+ // Currently, the only valid value is CAPABILITY_IAM, which is required for
+ // the following resources: AWS::IAM::AccessKey (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-accesskey.html),
+ // AWS::IAM::Group (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-group.html),
+ // AWS::IAM::InstanceProfile (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html),
+ // AWS::IAM::Policy (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html),
+ // AWS::IAM::Role (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html),
+ // AWS::IAM::User (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html),
+ // and AWS::IAM::UserToGroupAddition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-addusertogroup.html).
+ // If your stack template contains these resources, we recommend that you review
+ // all permissions associated with them and edit their permissions if necessary.
+ // If your template contains any of the listed resources and you don't specify
+ // this parameter, this action returns an InsufficientCapabilities error.
+ Capabilities []*string `type:"list"`
+
+ // The name of the change set. The name must be unique among all change sets
+ // that are associated with the specified stack.
+ //
+ // A change set name can contain only alphanumeric, case sensitive characters
+ // and hyphens. It must start with an alphabetic character and cannot exceed
+ // 128 characters.
+ ChangeSetName *string `min:"1" type:"string" required:"true"`
+
+ // A unique identifier for this CreateChangeSet request. Specify this token
+ // if you plan to retry requests so that AWS CloudFormation knows that you're
+ // not attempting to create another change set with the same name. You might
+ // retry CreateChangeSet requests to ensure that AWS CloudFormation successfully
+ // received them.
+ ClientToken *string `min:"1" type:"string"`
+
+ // A description to help you identify this change set.
+ Description *string `min:"1" type:"string"`
+
+ // The Amazon Resource Names (ARNs) of Amazon Simple Notification Service (Amazon
+ // SNS) topics that AWS CloudFormation associates with the stack. To remove
+ // all associated notification topics, specify an empty list.
+ NotificationARNs []*string `type:"list"`
+
+ // A list of Parameter structures that specify input parameters for the change
+ // set. For more information, see the Parameter (http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_Parameter.html)
+ // data type.
+ Parameters []*Parameter `type:"list"`
+
+ // The template resource types that you have permissions to work with if you
+ // execute this change set, such as AWS::EC2::Instance, AWS::EC2::*, or Custom::MyCustomInstance.
+ //
+ // If the list of resource types doesn't include a resource type that you're
+ // updating, the stack update fails. By default, AWS CloudFormation grants permissions
+ // to all resource types. AWS Identity and Access Management (IAM) uses this
+ // parameter for condition keys in IAM policies for AWS CloudFormation. For
+ // more information, see Controlling Access with AWS Identity and Access Management
+ // (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-template.html)
+ // in the AWS CloudFormation User Guide.
+ ResourceTypes []*string `type:"list"`
+
+ // The name or the unique ID of the stack for which you are creating a change
+ // set. AWS CloudFormation generates the change set by comparing this stack's
+ // information with the information that you submit, such as a modified template
+ // or different parameter input values.
+ StackName *string `min:"1" type:"string" required:"true"`
+
+ // Key-value pairs to associate with this stack. AWS CloudFormation also propagates
+ // these tags to resources in the stack. You can specify a maximum of 10 tags.
+ Tags []*Tag `type:"list"`
+
+ // A structure that contains the body of the revised template, with a minimum
+ // length of 1 byte and a maximum length of 51,200 bytes. AWS CloudFormation
+ // generates the change set by comparing this template with the template of
+ // the stack that you specified.
+ //
+ // Conditional: You must specify only TemplateBody or TemplateURL.
+ TemplateBody *string `min:"1" type:"string"`
+
+ // The location of the file that contains the revised template. The URL must
+ // point to a template (max size: 460,800 bytes) that is located in an S3 bucket.
+ // AWS CloudFormation generates the change set by comparing this template with
+ // the stack that you specified.
+ //
+ // Conditional: You must specify only TemplateBody or TemplateURL.
+ TemplateURL *string `min:"1" type:"string"`
+
+ // Whether to reuse the template that is associated with the stack to create
+ // the change set.
+ UsePreviousTemplate *bool `type:"boolean"`
+}
+
+// String returns the string representation
+func (s CreateChangeSetInput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s CreateChangeSetInput) GoString() string {
+ return s.String()
+}
+
+// The output for the CreateChangeSet action.
+type CreateChangeSetOutput struct {
+ _ struct{} `type:"structure"`
+
+ // The Amazon Resource Name (ARN) of the change set.
+ Id *string `min:"1" type:"string"`
+}
+
+// String returns the string representation
+func (s CreateChangeSetOutput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s CreateChangeSetOutput) GoString() string {
+ return s.String()
+}
+
// The input for CreateStack action.
type CreateStackInput struct {
_ struct{} `type:"structure"`
// A list of capabilities that you must specify before AWS CloudFormation can
- // create or update certain stacks. Some stack templates might include resources
- // that can affect permissions in your AWS account. For those stacks, you must
+ // create certain stacks. Some stack templates might include resources that
+ // can affect permissions in your AWS account, for example, by creating new
+ // AWS Identity and Access Management (IAM) users. For those stacks, you must
// explicitly acknowledge their capabilities by specifying this parameter.
//
// Currently, the only valid value is CAPABILITY_IAM, which is required for
@@ -777,8 +1135,9 @@ type CreateStackInput struct {
// AWS::IAM::User (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html),
// and AWS::IAM::UserToGroupAddition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-addusertogroup.html).
// If your stack template contains these resources, we recommend that you review
- // any permissions associated with them. If you don't specify this parameter,
- // this action returns an InsufficientCapabilities error.
+ // all permissions associated with them and edit their permissions if necessary.
+ // If your template contains any of the listed resources and you don't specify
+ // this parameter, this action returns an InsufficientCapabilities error.
Capabilities []*string `type:"list"`
// Set to true to disable rollback of the stack if stack creation failed. You
@@ -897,6 +1256,44 @@ func (s CreateStackOutput) GoString() string {
return s.String()
}
+// The input for the DeleteChangeSet action.
+type DeleteChangeSetInput struct {
+ _ struct{} `type:"structure"`
+
+ // The name or Amazon Resource Name (ARN) of the change set that you want to
+ // delete.
+ ChangeSetName *string `min:"1" type:"string" required:"true"`
+
+ // If you specified the name of a change set to delete, specify the stack name
+ // or ID (ARN) that is associated with it.
+ StackName *string `min:"1" type:"string"`
+}
+
+// String returns the string representation
+func (s DeleteChangeSetInput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s DeleteChangeSetInput) GoString() string {
+ return s.String()
+}
+
+// The output for the DeleteChangeSet action.
+type DeleteChangeSetOutput struct {
+ _ struct{} `type:"structure"`
+}
+
+// String returns the string representation
+func (s DeleteChangeSetOutput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s DeleteChangeSetOutput) GoString() string {
+ return s.String()
+}
+
// The input for DeleteStack action.
type DeleteStackInput struct {
_ struct{} `type:"structure"`
@@ -978,6 +1375,100 @@ func (s DescribeAccountLimitsOutput) GoString() string {
return s.String()
}
+// The input for the DescribeChangeSet action.
+type DescribeChangeSetInput struct {
+ _ struct{} `type:"structure"`
+
+ // The name or Amazon Resource Name (ARN) of the change set that you want to
+ // describe.
+ ChangeSetName *string `min:"1" type:"string" required:"true"`
+
+ // A string (provided by the DescribeChangeSet response output) that identifies
+ // the next page of information that you want to retrieve.
+ NextToken *string `min:"1" type:"string"`
+
+ // If you specified the name of a change set, specify the stack name or ID (ARN)
+ // of the change set you want to describe.
+ StackName *string `min:"1" type:"string"`
+}
+
+// String returns the string representation
+func (s DescribeChangeSetInput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s DescribeChangeSetInput) GoString() string {
+ return s.String()
+}
+
+// The output for the DescribeChangeSet action.
+type DescribeChangeSetOutput struct {
+ _ struct{} `type:"structure"`
+
+ // If you execute the change set, the list of capabilities that were explicitly
+ // acknowledged when the change set was created.
+ Capabilities []*string `type:"list"`
+
+ // The ARN of the change set.
+ ChangeSetId *string `min:"1" type:"string"`
+
+ // The name of the change set.
+ ChangeSetName *string `min:"1" type:"string"`
+
+ // A list of Change structures that describes the resources AWS CloudFormation
+ // changes if you execute the change set.
+ Changes []*Change `type:"list"`
+
+ // The start time when the change set was created, in UTC.
+ CreationTime *time.Time `type:"timestamp" timestampFormat:"iso8601"`
+
+ // Information about the change set.
+ Description *string `min:"1" type:"string"`
+
+ // If the output exceeds 1 MB, a string that identifies the next page of changes.
+ // If there is no additional page, this value is null.
+ NextToken *string `min:"1" type:"string"`
+
+ // The ARNs of the Amazon Simple Notification Service (Amazon SNS) topics that
+ // will be associated with the stack if you execute the change set.
+ NotificationARNs []*string `type:"list"`
+
+ // A list of Parameter structures that describes the input parameters and their
+ // values used to create the change set. For more information, see the Parameter
+ // (http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_Parameter.html)
+ // data type.
+ Parameters []*Parameter `type:"list"`
+
+ // The ARN of the stack that is associated with the change set.
+ StackId *string `type:"string"`
+
+ // The name of the stack that is associated with the change set.
+ StackName *string `type:"string"`
+
+ // The current status of the change set, such as CREATE_IN_PROGRESS, CREATE_COMPLETE,
+ // or FAILED.
+ Status *string `type:"string" enum:"ChangeSetStatus"`
+
+ // A description of the change set's status. For example, if your attempt to
+ // create a change set failed, AWS CloudFormation shows the error message.
+ StatusReason *string `type:"string"`
+
+ // If you execute the change set, the tags that will be associated with the
+ // stack.
+ Tags []*Tag `type:"list"`
+}
+
+// String returns the string representation
+func (s DescribeChangeSetOutput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s DescribeChangeSetOutput) GoString() string {
+ return s.String()
+}
+
// The input for DescribeStackEvents action.
type DescribeStackEventsInput struct {
_ struct{} `type:"structure"`
@@ -1238,6 +1729,44 @@ func (s EstimateTemplateCostOutput) GoString() string {
return s.String()
}
+// The input for the ExecuteChangeSet action.
+type ExecuteChangeSetInput struct {
+ _ struct{} `type:"structure"`
+
+ // The name or ARN of the change set that you want use to update the specified
+ // stack.
+ ChangeSetName *string `min:"1" type:"string" required:"true"`
+
+ // If you specified the name of a change set, specify the stack name or ID (ARN)
+ // that is associated with the change set you want to execute.
+ StackName *string `min:"1" type:"string"`
+}
+
+// String returns the string representation
+func (s ExecuteChangeSetInput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ExecuteChangeSetInput) GoString() string {
+ return s.String()
+}
+
+// The output for the ExecuteChangeSet action.
+type ExecuteChangeSetOutput struct {
+ _ struct{} `type:"structure"`
+}
+
+// String returns the string representation
+func (s ExecuteChangeSetOutput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ExecuteChangeSetOutput) GoString() string {
+ return s.String()
+}
+
// The input for the GetStackPolicy action.
type GetStackPolicyInput struct {
_ struct{} `type:"structure"`
@@ -1378,7 +1907,7 @@ type GetTemplateSummaryOutput struct {
CapabilitiesReason *string `type:"string"`
// The value that is defined in the Description property of the template.
- Description *string `type:"string"`
+ Description *string `min:"1" type:"string"`
// The value that is defined for the Metadata property of the template.
Metadata *string `type:"string"`
@@ -1406,6 +1935,52 @@ func (s GetTemplateSummaryOutput) GoString() string {
return s.String()
}
+// The input for the ListChangeSets action.
+type ListChangeSetsInput struct {
+ _ struct{} `type:"structure"`
+
+ // A string (provided by the ListChangeSets response output) that identifies
+ // the next page of change sets that you want to retrieve.
+ NextToken *string `min:"1" type:"string"`
+
+ // The name or the Amazon Resource Name (ARN) of the stack for which you want
+ // to list change sets.
+ StackName *string `min:"1" type:"string" required:"true"`
+}
+
+// String returns the string representation
+func (s ListChangeSetsInput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ListChangeSetsInput) GoString() string {
+ return s.String()
+}
+
+// The output for the ListChangeSets action.
+type ListChangeSetsOutput struct {
+ _ struct{} `type:"structure"`
+
+ // If the output exceeds 1 MB, a string that identifies the next page of change
+ // sets. If there is no additional page, this value is null.
+ NextToken *string `min:"1" type:"string"`
+
+ // A list of ChangeSetSummary structures that provides the ID and status of
+ // each change set for the specified stack.
+ Summaries []*ChangeSetSummary `type:"list"`
+}
+
+// String returns the string representation
+func (s ListChangeSetsOutput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ListChangeSetsOutput) GoString() string {
+ return s.String()
+}
+
// The input for the ListStackResource action.
type ListStackResourcesInput struct {
_ struct{} `type:"structure"`
@@ -1437,8 +2012,8 @@ func (s ListStackResourcesInput) GoString() string {
type ListStackResourcesOutput struct {
_ struct{} `type:"structure"`
- // If the output exceeds 1 MB in size, a string that identifies the next page
- // of stack resources. If no additional page exists, this value is null.
+ // If the output exceeds 1 MB, a string that identifies the next page of stack
+ // resources. If no additional page exists, this value is null.
NextToken *string `min:"1" type:"string"`
// A list of StackResourceSummary structures.
@@ -1506,7 +2081,7 @@ type Output struct {
_ struct{} `type:"structure"`
// User defined description associated with the output.
- Description *string `type:"string"`
+ Description *string `min:"1" type:"string"`
// The key associated with the output.
OutputKey *string `type:"string"`
@@ -1581,7 +2156,7 @@ type ParameterDeclaration struct {
DefaultValue *string `type:"string"`
// The description that is associate with the parameter.
- Description *string `type:"string"`
+ Description *string `min:"1" type:"string"`
// Flag that indicates whether the parameter value is shown as plain text in
// logs and in the AWS Management Console.
@@ -1607,6 +2182,152 @@ func (s ParameterDeclaration) GoString() string {
return s.String()
}
+// The ResourceChange structure describes the resource and the action that AWS
+// CloudFormation will perform on it if you execute this change set.
+type ResourceChange struct {
+ _ struct{} `type:"structure"`
+
+ // The action that AWS CloudFormation takes on the resource, such as Add (adds
+ // a new resource), Modify (changes a resource), or Remove (deletes a resource).
+ Action *string `type:"string" enum:"ChangeAction"`
+
+ // For the Modify action, a list of ResourceChangeDetail structures that describes
+ // the changes that AWS CloudFormation will make to the resource.
+ Details []*ResourceChangeDetail `type:"list"`
+
+ // The resource's logical ID, which is defined in the stack's template.
+ LogicalResourceId *string `type:"string"`
+
+ // The resource's physical ID (resource name). Resources that you are adding
+ // don't have physical IDs because they haven't been created.
+ PhysicalResourceId *string `type:"string"`
+
+ // For the Modify action, indicates whether AWS CloudFormation will replace
+ // the resource by creating a new one and deleting the old one. This value depends
+ // on the value of the RequiresRecreation property in the ResourceTargetDefinition
+ // structure. For example, if the RequiresRecreation field is Always and the
+ // Evaluation field is Static, Replacement is True. If the RequiresRecreation
+ // field is Always and the Evaluation field is Dynamic, Replacement is Conditionally.
+ //
+ // If you have multiple changes with different RequiresRecreation values, the
+ // Replacement value depends on the change with the most impact. A RequiresRecreation
+ // value of Always has the most impact, followed by Conditionally, and then
+ // Never.
+ Replacement *string `type:"string" enum:"Replacement"`
+
+ // The type of AWS CloudFormation resource, such as AWS::S3::Bucket.
+ ResourceType *string `min:"1" type:"string"`
+
+ // For the Modify action, indicates which resource attribute is triggering this
+ // update, such as a change in the resource attribute's Metadata, Properties,
+ // or Tags.
+ Scope []*string `type:"list"`
+}
+
+// String returns the string representation
+func (s ResourceChange) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ResourceChange) GoString() string {
+ return s.String()
+}
+
+// For a resource with Modify as the action, the ResourceChange structure describes
+// the changes AWS CloudFormation will make to that resource.
+type ResourceChangeDetail struct {
+ _ struct{} `type:"structure"`
+
+ // The identity of the entity that triggered this change. This entity is a member
+ // of the group that is specified by the ChangeSource field. For example, if
+ // you modified the value of the KeyPairName parameter, the CausingEntity is
+ // the name of the parameter (KeyPairName).
+ //
+ // If the ChangeSource value is DirectModification, no value is given for CausingEntity.
+ CausingEntity *string `type:"string"`
+
+ // The group to which the CausingEntity value belongs. There are five entity
+ // groups:
+ //
+ // ResourceReference entities are Ref intrinsic functions that refer to resources
+ // in the template, such as { "Ref" : "MyEC2InstanceResource" }. ParameterReference
+ // entities are Ref intrinsic functions that get template parameter values,
+ // such as { "Ref" : "MyPasswordParameter" }. ResourceAttribute entities are
+ // Fn::GetAtt intrinsic functions that get resource attribute values, such as
+ // { "Fn::GetAtt" : [ "MyEC2InstanceResource", "PublicDnsName" ] }. DirectModification
+ // entities are changes that are made directly to the template. Automatic entities
+ // are AWS::CloudFormation::Stack resource types, which are also known as nested
+ // stacks. If you made no changes to the AWS::CloudFormation::Stack resource,
+ // AWS CloudFormation sets the ChangeSource to Automatic because the nested
+ // stack's template might have changed. Changes to a nested stack's template
+ // aren't visible to AWS CloudFormation until you run an update on the parent
+ // stack.
+ ChangeSource *string `type:"string" enum:"ChangeSource"`
+
+ // Indicates whether AWS CloudFormation can determine the target value, and
+ // whether the target value will change before you execute a change set.
+ //
+ // For Static evaluations, AWS CloudFormation can determine that the target
+ // value will change, and its value. For example, if you directly modify the
+ // InstanceType property of an EC2 instance, AWS CloudFormation knows that this
+ // property value will change, and its value, so this is a Static evaluation.
+ //
+ // For Dynamic evaluations, cannot determine the target value because it depends
+ // on the result of an intrinsic function, such as a Ref or Fn::GetAtt intrinsic
+ // function, when the stack is updated. For example, if your template includes
+ // a reference to a resource that is conditionally recreated, the value of the
+ // reference (the physical ID of the resource) might change, depending on if
+ // the resource is recreated. If the resource is recreated, it will have a new
+ // physical ID, so all references to that resource will also be updated.
+ Evaluation *string `type:"string" enum:"EvaluationType"`
+
+ // A ResourceTargetDefinition structure that describes the field that AWS CloudFormation
+ // will change and whether the resource will be recreated.
+ Target *ResourceTargetDefinition `type:"structure"`
+}
+
+// String returns the string representation
+func (s ResourceChangeDetail) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ResourceChangeDetail) GoString() string {
+ return s.String()
+}
+
+// The field that AWS CloudFormation will change, such as the name of a resource's
+// property, and whether the resource will be recreated.
+type ResourceTargetDefinition struct {
+ _ struct{} `type:"structure"`
+
+ // Indicates which resource attribute is triggering this update, such as a change
+ // in the resource attribute's Metadata, Properties, or Tags.
+ Attribute *string `type:"string" enum:"ResourceAttribute"`
+
+ // If the Attribute value is Properties, the name of the property. For all other
+ // attributes, the value is null.
+ Name *string `type:"string"`
+
+ // If the Attribute value is Properties, indicates whether a change to this
+ // property causes the resource to be recreated. The value can be Never, Always,
+ // or Conditionally. To determine the conditions for a Conditionally recreation,
+ // see the update behavior for that property (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)
+ // in the AWS CloudFormation User Guide.
+ RequiresRecreation *string `type:"string" enum:"RequiresRecreation"`
+}
+
+// String returns the string representation
+func (s ResourceTargetDefinition) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ResourceTargetDefinition) GoString() string {
+ return s.String()
+}
+
// The input for the SetStackPolicy action.
type SetStackPolicyInput struct {
_ struct{} `type:"structure"`
@@ -1621,7 +2342,7 @@ type SetStackPolicyInput struct {
StackPolicyBody *string `min:"1" type:"string"`
// Location of a file containing the stack policy. The URL must point to a policy
- // (max size: 16KB) located in an S3 bucket in the same region as the stack.
+ // (maximum size: 16 KB) located in an S3 bucket in the same region as the stack.
// You can specify either the StackPolicyBody or the StackPolicyURL parameter,
// but not both.
StackPolicyURL *string `min:"1" type:"string"`
@@ -1709,7 +2430,7 @@ type Stack struct {
CreationTime *time.Time `type:"timestamp" timestampFormat:"iso8601" required:"true"`
// A user-defined description associated with the stack.
- Description *string `type:"string"`
+ Description *string `min:"1" type:"string"`
// Boolean to enable or disable rollback on stack creation failures:
//
@@ -1741,7 +2462,7 @@ type Stack struct {
// Success/failure message associated with the stack status.
StackStatusReason *string `type:"string"`
- // A list of Tags that specify cost allocation information for the stack.
+ // A list of Tags that specify information about the stack.
Tags []*Tag `type:"list"`
// The amount of time within which stack creation should complete.
@@ -1784,7 +2505,7 @@ type StackEvent struct {
// Type of resource. (For more information, go to AWS Resource Types Reference
// (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)
// in the AWS CloudFormation User Guide.)
- ResourceType *string `type:"string"`
+ ResourceType *string `min:"1" type:"string"`
// The unique ID name of the instance of the stack.
StackId *string `type:"string" required:"true"`
@@ -1811,7 +2532,7 @@ type StackResource struct {
_ struct{} `type:"structure"`
// User defined description associated with the resource.
- Description *string `type:"string"`
+ Description *string `min:"1" type:"string"`
// The logical name of the resource specified in the template.
LogicalResourceId *string `type:"string" required:"true"`
@@ -1829,7 +2550,7 @@ type StackResource struct {
// Type of resource. (For more information, go to AWS Resource Types Reference
// (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)
// in the AWS CloudFormation User Guide.)
- ResourceType *string `type:"string" required:"true"`
+ ResourceType *string `min:"1" type:"string" required:"true"`
// Unique identifier of the stack.
StackId *string `type:"string"`
@@ -1856,7 +2577,7 @@ type StackResourceDetail struct {
_ struct{} `type:"structure"`
// User defined description associated with the resource.
- Description *string `type:"string"`
+ Description *string `min:"1" type:"string"`
// Time the status was updated.
LastUpdatedTimestamp *time.Time `type:"timestamp" timestampFormat:"iso8601" required:"true"`
@@ -1882,7 +2603,7 @@ type StackResourceDetail struct {
// Type of resource. ((For more information, go to AWS Resource Types Reference
// (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)
// in the AWS CloudFormation User Guide.)
- ResourceType *string `type:"string" required:"true"`
+ ResourceType *string `min:"1" type:"string" required:"true"`
// Unique identifier of the stack.
StackId *string `type:"string"`
@@ -1924,7 +2645,7 @@ type StackResourceSummary struct {
// Type of resource. (For more information, go to AWS Resource Types Reference
// (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)
// in the AWS CloudFormation User Guide.)
- ResourceType *string `type:"string" required:"true"`
+ ResourceType *string `min:"1" type:"string" required:"true"`
}
// String returns the string representation
@@ -1977,9 +2698,8 @@ func (s StackSummary) GoString() string {
return s.String()
}
-// The Tag type is used by CreateStack in the Tags parameter. It allows you
-// to specify a key-value pair that can be used to store information related
-// to cost allocation for an AWS CloudFormation stack.
+// The Tag type enables you to specify a key-value pair that can be used to
+// store information about an AWS CloudFormation stack.
type Tag struct {
_ struct{} `type:"structure"`
@@ -2011,7 +2731,7 @@ type TemplateParameter struct {
DefaultValue *string `type:"string"`
// User defined description associated with the parameter.
- Description *string `type:"string"`
+ Description *string `min:"1" type:"string"`
// Flag indicating whether the parameter should be displayed as plain text in
// logs and UIs.
@@ -2036,11 +2756,13 @@ type UpdateStackInput struct {
_ struct{} `type:"structure"`
// A list of capabilities that you must specify before AWS CloudFormation can
- // create or update certain stacks. Some stack templates might include resources
- // that can affect permissions in your AWS account. For those stacks, you must
- // explicitly acknowledge their capabilities by specifying this parameter. Currently,
- // the only valid value is CAPABILITY_IAM, which is required for the following
- // resources: AWS::IAM::AccessKey (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-accesskey.html),
+ // update certain stacks. Some stack templates might include resources that
+ // can affect permissions in your AWS account, for example, by creating new
+ // AWS Identity and Access Management (IAM) users. For those stacks, you must
+ // explicitly acknowledge their capabilities by specifying this parameter.
+ //
+ // Currently, the only valid value is CAPABILITY_IAM, which is required for
+ // the following resources: AWS::IAM::AccessKey (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-accesskey.html),
// AWS::IAM::Group (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-group.html),
// AWS::IAM::InstanceProfile (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html),
// AWS::IAM::Policy (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html),
@@ -2048,8 +2770,9 @@ type UpdateStackInput struct {
// AWS::IAM::User (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html),
// and AWS::IAM::UserToGroupAddition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-addusertogroup.html).
// If your stack template contains these resources, we recommend that you review
- // any permissions associated with them. If you don't specify this parameter,
- // this action returns an InsufficientCapabilities error.
+ // all permissions associated with them and edit their permissions if necessary.
+ // If your template contains any of the listed resources and you don't specify
+ // this parameter, this action returns an InsufficientCapabilities error.
Capabilities []*string `type:"list"`
// Amazon Simple Notification Service topic Amazon Resource Names (ARNs) that
@@ -2222,7 +2945,7 @@ type ValidateTemplateOutput struct {
CapabilitiesReason *string `type:"string"`
// The description found within the template.
- Description *string `type:"string"`
+ Description *string `min:"1" type:"string"`
// A list of TemplateParameter structures.
Parameters []*TemplateParameter `type:"list"`
@@ -2243,6 +2966,53 @@ const (
CapabilityCapabilityIam = "CAPABILITY_IAM"
)
+const (
+ // @enum ChangeAction
+ ChangeActionAdd = "Add"
+ // @enum ChangeAction
+ ChangeActionModify = "Modify"
+ // @enum ChangeAction
+ ChangeActionRemove = "Remove"
+)
+
+const (
+ // @enum ChangeSetStatus
+ ChangeSetStatusCreatePending = "CREATE_PENDING"
+ // @enum ChangeSetStatus
+ ChangeSetStatusCreateInProgress = "CREATE_IN_PROGRESS"
+ // @enum ChangeSetStatus
+ ChangeSetStatusCreateComplete = "CREATE_COMPLETE"
+ // @enum ChangeSetStatus
+ ChangeSetStatusDeleteComplete = "DELETE_COMPLETE"
+ // @enum ChangeSetStatus
+ ChangeSetStatusFailed = "FAILED"
+)
+
+const (
+ // @enum ChangeSource
+ ChangeSourceResourceReference = "ResourceReference"
+ // @enum ChangeSource
+ ChangeSourceParameterReference = "ParameterReference"
+ // @enum ChangeSource
+ ChangeSourceResourceAttribute = "ResourceAttribute"
+ // @enum ChangeSource
+ ChangeSourceDirectModification = "DirectModification"
+ // @enum ChangeSource
+ ChangeSourceAutomatic = "Automatic"
+)
+
+const (
+ // @enum ChangeType
+ ChangeTypeResource = "Resource"
+)
+
+const (
+ // @enum EvaluationType
+ EvaluationTypeStatic = "Static"
+ // @enum EvaluationType
+ EvaluationTypeDynamic = "Dynamic"
+)
+
const (
// @enum OnFailure
OnFailureDoNothing = "DO_NOTHING"
@@ -2252,6 +3022,39 @@ const (
OnFailureDelete = "DELETE"
)
+const (
+ // @enum Replacement
+ ReplacementTrue = "True"
+ // @enum Replacement
+ ReplacementFalse = "False"
+ // @enum Replacement
+ ReplacementConditional = "Conditional"
+)
+
+const (
+ // @enum RequiresRecreation
+ RequiresRecreationNever = "Never"
+ // @enum RequiresRecreation
+ RequiresRecreationConditionally = "Conditionally"
+ // @enum RequiresRecreation
+ RequiresRecreationAlways = "Always"
+)
+
+const (
+ // @enum ResourceAttribute
+ ResourceAttributeProperties = "Properties"
+ // @enum ResourceAttribute
+ ResourceAttributeMetadata = "Metadata"
+ // @enum ResourceAttribute
+ ResourceAttributeCreationPolicy = "CreationPolicy"
+ // @enum ResourceAttribute
+ ResourceAttributeUpdatePolicy = "UpdatePolicy"
+ // @enum ResourceAttribute
+ ResourceAttributeDeletionPolicy = "DeletionPolicy"
+ // @enum ResourceAttribute
+ ResourceAttributeTags = "Tags"
+)
+
const (
// @enum ResourceSignalStatus
ResourceSignalStatusSuccess = "SUCCESS"
diff --git a/vendor/github.com/aws/aws-sdk-go/service/cloudformation/waiters.go b/vendor/github.com/aws/aws-sdk-go/service/cloudformation/waiters.go
index cd90e41d7af8..f8ca675144dd 100644
--- a/vendor/github.com/aws/aws-sdk-go/service/cloudformation/waiters.go
+++ b/vendor/github.com/aws/aws-sdk-go/service/cloudformation/waiters.go
@@ -10,7 +10,7 @@ func (c *CloudFormation) WaitUntilStackCreateComplete(input *DescribeStacksInput
waiterCfg := waiter.Config{
Operation: "DescribeStacks",
Delay: 30,
- MaxAttempts: 50,
+ MaxAttempts: 120,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
@@ -24,6 +24,48 @@ func (c *CloudFormation) WaitUntilStackCreateComplete(input *DescribeStacksInput
Argument: "Stacks[].StackStatus",
Expected: "CREATE_FAILED",
},
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "DELETE_COMPLETE",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "DELETE_IN_PROGRESS",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "DELETE_FAILED",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "ROLLBACK_COMPLETE",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "ROLLBACK_FAILED",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "ROLLBACK_IN_PROGRESS",
+ },
+ {
+ State: "failure",
+ Matcher: "error",
+ Argument: "",
+ Expected: "ValidationError",
+ },
},
}
@@ -39,7 +81,7 @@ func (c *CloudFormation) WaitUntilStackDeleteComplete(input *DescribeStacksInput
waiterCfg := waiter.Config{
Operation: "DescribeStacks",
Delay: 30,
- MaxAttempts: 25,
+ MaxAttempts: 120,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
@@ -59,6 +101,113 @@ func (c *CloudFormation) WaitUntilStackDeleteComplete(input *DescribeStacksInput
Argument: "Stacks[].StackStatus",
Expected: "DELETE_FAILED",
},
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "CREATE_COMPLETE",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "CREATE_FAILED",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "CREATE_IN_PROGRESS",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "ROLLBACK_COMPLETE",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "ROLLBACK_FAILED",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "ROLLBACK_IN_PROGRESS",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_COMPLETE",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_IN_PROGRESS",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_ROLLBACK_COMPLETE",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_ROLLBACK_FAILED",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_ROLLBACK_IN_PROGRESS",
+ },
+ },
+ }
+
+ w := waiter.Waiter{
+ Client: c,
+ Input: input,
+ Config: waiterCfg,
+ }
+ return w.Wait()
+}
+
+func (c *CloudFormation) WaitUntilStackExists(input *DescribeStacksInput) error {
+ waiterCfg := waiter.Config{
+ Operation: "DescribeStacks",
+ Delay: 5,
+ MaxAttempts: 20,
+ Acceptors: []waiter.WaitAcceptor{
+ {
+ State: "success",
+ Matcher: "status",
+ Argument: "",
+ Expected: 200,
+ },
+ {
+ State: "retry",
+ Matcher: "error",
+ Argument: "",
+ Expected: "ValidationError",
+ },
},
}
@@ -74,7 +223,7 @@ func (c *CloudFormation) WaitUntilStackUpdateComplete(input *DescribeStacksInput
waiterCfg := waiter.Config{
Operation: "DescribeStacks",
Delay: 30,
- MaxAttempts: 5,
+ MaxAttempts: 120,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
@@ -88,6 +237,36 @@ func (c *CloudFormation) WaitUntilStackUpdateComplete(input *DescribeStacksInput
Argument: "Stacks[].StackStatus",
Expected: "UPDATE_FAILED",
},
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_ROLLBACK_COMPLETE",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_ROLLBACK_FAILED",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS",
+ },
+ {
+ State: "failure",
+ Matcher: "pathAny",
+ Argument: "Stacks[].StackStatus",
+ Expected: "UPDATE_ROLLBACK_IN_PROGRESS",
+ },
+ {
+ State: "failure",
+ Matcher: "error",
+ Argument: "",
+ Expected: "ValidationError",
+ },
},
}
diff --git a/vendor/github.com/aws/aws-sdk-go/service/codedeploy/api.go b/vendor/github.com/aws/aws-sdk-go/service/codedeploy/api.go
index 7bc6da339abc..c4cf4891961a 100644
--- a/vendor/github.com/aws/aws-sdk-go/service/codedeploy/api.go
+++ b/vendor/github.com/aws/aws-sdk-go/service/codedeploy/api.go
@@ -2803,7 +2803,7 @@ type ListDeploymentsInput struct {
// queued deployments in the resulting list. In Progress: Include in-progress
// deployments in the resulting list. Succeeded: Include successful deployments
// in the resulting list. Failed: Include failed deployments in the resulting
- // list. Aborted: Include aborted deployments in the resulting list.
+ // list. Stopped: Include stopped deployments in the resulting list.
IncludeOnlyStatuses []*string `locationName:"includeOnlyStatuses" type:"list"`
// An identifier returned from the previous list deployments call. It can be
diff --git a/vendor/github.com/aws/aws-sdk-go/service/elasticache/api.go b/vendor/github.com/aws/aws-sdk-go/service/elasticache/api.go
index c381d3381137..907aa394d55f 100644
--- a/vendor/github.com/aws/aws-sdk-go/service/elasticache/api.go
+++ b/vendor/github.com/aws/aws-sdk-go/service/elasticache/api.go
@@ -1884,8 +1884,6 @@ type CopySnapshotInput struct {
// The name of an existing snapshot from which to copy.
SourceSnapshotName *string `type:"string" required:"true"`
- TargetBucket *string `type:"string"`
-
// A name for the copied snapshot.
TargetSnapshotName *string `type:"string" required:"true"`
}
@@ -4737,7 +4735,7 @@ type ResetCacheParameterGroupInput struct {
// An array of parameter names to be reset. If you are not resetting the entire
// cache parameter group, you must specify at least one parameter name.
- ParameterNameValues []*ParameterNameValue `locationNameList:"ParameterNameValue" type:"list"`
+ ParameterNameValues []*ParameterNameValue `locationNameList:"ParameterNameValue" type:"list" required:"true"`
// If true, all parameters in the cache parameter group will be reset to default
// values. If false, no such action occurs.
diff --git a/vendor/github.com/aws/aws-sdk-go/service/elb/waiters.go b/vendor/github.com/aws/aws-sdk-go/service/elb/waiters.go
index 5d5755d049a0..b1c9a526aaec 100644
--- a/vendor/github.com/aws/aws-sdk-go/service/elb/waiters.go
+++ b/vendor/github.com/aws/aws-sdk-go/service/elb/waiters.go
@@ -29,6 +29,35 @@ func (c *ELB) WaitUntilAnyInstanceInService(input *DescribeInstanceHealthInput)
return w.Wait()
}
+func (c *ELB) WaitUntilInstanceDeregistered(input *DescribeInstanceHealthInput) error {
+ waiterCfg := waiter.Config{
+ Operation: "DescribeInstanceHealth",
+ Delay: 15,
+ MaxAttempts: 40,
+ Acceptors: []waiter.WaitAcceptor{
+ {
+ State: "success",
+ Matcher: "pathAll",
+ Argument: "InstanceStates[].State",
+ Expected: "OutOfService",
+ },
+ {
+ State: "success",
+ Matcher: "error",
+ Argument: "",
+ Expected: "InvalidInstance",
+ },
+ },
+ }
+
+ w := waiter.Waiter{
+ Client: c,
+ Input: input,
+ Config: waiterCfg,
+ }
+ return w.Wait()
+}
+
func (c *ELB) WaitUntilInstanceInService(input *DescribeInstanceHealthInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeInstanceHealth",
diff --git a/vendor/github.com/aws/aws-sdk-go/service/redshift/api.go b/vendor/github.com/aws/aws-sdk-go/service/redshift/api.go
index 69315d35e8a0..0a250ba953eb 100644
--- a/vendor/github.com/aws/aws-sdk-go/service/redshift/api.go
+++ b/vendor/github.com/aws/aws-sdk-go/service/redshift/api.go
@@ -1696,9 +1696,9 @@ func (c *Redshift) DescribeTableRestoreStatusRequest(input *DescribeTableRestore
// Lists the status of one or more table restore requests made using the RestoreTableFromClusterSnapshot
// API action. If you don't specify a value for the TableRestoreRequestId parameter,
-// then DescribeTableRestoreStatus returns the status of all in-progress table
-// restore requests. Otherwise DescribeTableRestoreStatus returns the status
-// of the table specified by TableRestoreRequestId.
+// then DescribeTableRestoreStatus returns the status of all table restore requests
+// ordered by the date and time of the request in ascending order. Otherwise
+// DescribeTableRestoreStatus returns the status of the table specified by TableRestoreRequestId.
func (c *Redshift) DescribeTableRestoreStatus(input *DescribeTableRestoreStatusInput) (*DescribeTableRestoreStatusOutput, error) {
req, out := c.DescribeTableRestoreStatusRequest(input)
err := req.Send()
@@ -1903,6 +1903,36 @@ func (c *Redshift) ModifyCluster(input *ModifyClusterInput) (*ModifyClusterOutpu
return out, err
}
+const opModifyClusterIamRoles = "ModifyClusterIamRoles"
+
+// ModifyClusterIamRolesRequest generates a request for the ModifyClusterIamRoles operation.
+func (c *Redshift) ModifyClusterIamRolesRequest(input *ModifyClusterIamRolesInput) (req *request.Request, output *ModifyClusterIamRolesOutput) {
+ op := &request.Operation{
+ Name: opModifyClusterIamRoles,
+ HTTPMethod: "POST",
+ HTTPPath: "/",
+ }
+
+ if input == nil {
+ input = &ModifyClusterIamRolesInput{}
+ }
+
+ req = c.newRequest(op, input, output)
+ output = &ModifyClusterIamRolesOutput{}
+ req.Data = output
+ return
+}
+
+// Modifies the list of AWS Identity and Access Management (IAM) roles that
+// can be used by the cluster to access other AWS services.
+//
+// A cluster can have up to 10 IAM roles associated at any time.
+func (c *Redshift) ModifyClusterIamRoles(input *ModifyClusterIamRolesInput) (*ModifyClusterIamRolesOutput, error) {
+ req, out := c.ModifyClusterIamRolesRequest(input)
+ err := req.Send()
+ return out, err
+}
+
const opModifyClusterParameterGroup = "ModifyClusterParameterGroup"
// ModifyClusterParameterGroupRequest generates a request for the ModifyClusterParameterGroup operation.
@@ -2492,6 +2522,10 @@ type Cluster struct {
// Values: active, applying
HsmStatus *HsmStatus `type:"structure"`
+ // A list of AWS Identity and Access Management (IAM) roles that can be used
+ // by the cluster to access other AWS services.
+ IamRoles []*ClusterIamRole `locationNameList:"ClusterIamRole" type:"list"`
+
// The AWS Key Management Service (KMS) key ID of the encryption key used to
// encrypt data in the cluster.
KmsKeyId *string `type:"string"`
@@ -2545,6 +2579,34 @@ func (s Cluster) GoString() string {
return s.String()
}
+// An AWS Identity and Access Management (IAM) role that can be used by the
+// associated Amazon Redshift cluster to access other AWS services.
+type ClusterIamRole struct {
+ _ struct{} `type:"structure"`
+
+ // Describes the status of the IAM role's association with an Amazon Redshift
+ // cluster.
+ //
+ // The following are possible statuses and descriptions. in-sync: The role
+ // is available for use by the cluster. adding: The role is in the process of
+ // being associated with the cluster. removing: The role is in the process of
+ // being disassociated with the cluster.
+ ApplyStatus *string `type:"string"`
+
+ // The Amazon Resource Name (ARN) of the IAM role. For example, arn:aws:iam::123456789012:role/RedshiftCopyUnload.
+ IamRoleArn *string `type:"string"`
+}
+
+// String returns the string representation
+func (s ClusterIamRole) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ClusterIamRole) GoString() string {
+ return s.String()
+}
+
// The identifier of a node in a cluster.
type ClusterNode struct {
_ struct{} `type:"structure"`
@@ -3011,6 +3073,14 @@ type CreateClusterInput struct {
// the Amazon Redshift cluster can use to retrieve and store keys in an HSM.
HsmConfigurationIdentifier *string `type:"string"`
+ // A list of AWS Identity and Access Management (IAM) roles that can be used
+ // by the cluster to access other AWS services. You must supply the IAM roles
+ // in their Amazon Resource Name (ARN) format. You can supply up to 10 IAM roles
+ // in a single request.
+ //
+ // A cluster can have up to 10 IAM roles associated at any time.
+ IamRoles []*string `locationNameList:"IamRoleArn" type:"list"`
+
// The AWS Key Management Service (KMS) key ID of the encryption key that you
// want to use to encrypt data in the cluster.
KmsKeyId *string `type:"string"`
@@ -6005,6 +6075,50 @@ func (s LoggingStatus) GoString() string {
return s.String()
}
+type ModifyClusterIamRolesInput struct {
+ _ struct{} `type:"structure"`
+
+ // Zero or more IAM roles (in their ARN format) to associate with the cluster.
+ // You can associate up to 10 IAM roles with a single cluster in a single request.
+ AddIamRoles []*string `locationNameList:"IamRoleArn" type:"list"`
+
+ // The unique identifier of the cluster for which you want to associate or disassociate
+ // IAM roles.
+ ClusterIdentifier *string `type:"string" required:"true"`
+
+ // Zero or more IAM roles (in their ARN format) to disassociate from the cluster.
+ // You can disassociate up to 10 IAM roles from a single cluster in a single
+ // request.
+ RemoveIamRoles []*string `locationNameList:"IamRoleArn" type:"list"`
+}
+
+// String returns the string representation
+func (s ModifyClusterIamRolesInput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ModifyClusterIamRolesInput) GoString() string {
+ return s.String()
+}
+
+type ModifyClusterIamRolesOutput struct {
+ _ struct{} `type:"structure"`
+
+ // Describes a cluster.
+ Cluster *Cluster `type:"structure"`
+}
+
+// String returns the string representation
+func (s ModifyClusterIamRolesOutput) String() string {
+ return awsutil.Prettify(s)
+}
+
+// GoString returns the string representation
+func (s ModifyClusterIamRolesOutput) GoString() string {
+ return s.String()
+}
+
type ModifyClusterInput struct {
_ struct{} `type:"structure"`
@@ -6816,6 +6930,14 @@ type RestoreFromClusterSnapshotInput struct {
// the Amazon Redshift cluster can use to retrieve and store keys in an HSM.
HsmConfigurationIdentifier *string `type:"string"`
+ // A list of AWS Identity and Access Management (IAM) roles that can be used
+ // by the cluster to access other AWS services. You must supply the IAM roles
+ // in their Amazon Resource Name (ARN) format. You can supply up to 10 IAM roles
+ // in a single request.
+ //
+ // A cluster can have up to 10 IAM roles associated at any time.
+ IamRoles []*string `locationNameList:"IamRoleArn" type:"list"`
+
// The AWS Key Management Service (KMS) key ID of the encryption key that you
// want to use to encrypt data in the cluster that you restore from a shared
// snapshot.
@@ -6965,7 +7087,8 @@ type RestoreTableFromClusterSnapshotInput struct {
// The name of the source database that contains the table to restore from.
SourceDatabaseName *string `type:"string" required:"true"`
- // The name of the source schema that contains the table to restore from.
+ // The name of the source schema that contains the table to restore from. If
+ // you do not specify a SourceSchemaName value, the default is public.
SourceSchemaName *string `type:"string"`
// The name of the source table to restore from.
@@ -7316,7 +7439,7 @@ type TableRestoreStatus struct {
ClusterIdentifier *string `type:"string"`
// A description of the status of the table restore request. Status values include
- // SUCCEEDED, FAILED, CANCELLED, PENDING, IN_PROGRESS.
+ // SUCCEEDED, FAILED, CANCELED, PENDING, IN_PROGRESS.
Message *string `type:"string"`
// The name of the table to create as a result of the table restore request.
@@ -7343,7 +7466,7 @@ type TableRestoreStatus struct {
// A value that describes the current state of the table restore request.
//
- // Valid Values: SUCCEEDED, FAILED, CANCELLED, PENDING, IN_PROGRESS
+ // Valid Values: SUCCEEDED, FAILED, CANCELED, PENDING, IN_PROGRESS
Status *string `type:"string" enum:"TableRestoreStatusType"`
// The unique identifier for the table restore request.
diff --git a/vendor/github.com/aws/aws-sdk-go/service/route53/waiters.go b/vendor/github.com/aws/aws-sdk-go/service/route53/waiters.go
new file mode 100644
index 000000000000..04786169e2a6
--- /dev/null
+++ b/vendor/github.com/aws/aws-sdk-go/service/route53/waiters.go
@@ -0,0 +1,30 @@
+// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
+
+package route53
+
+import (
+ "github.com/aws/aws-sdk-go/private/waiter"
+)
+
+func (c *Route53) WaitUntilResourceRecordSetsChanged(input *GetChangeInput) error {
+ waiterCfg := waiter.Config{
+ Operation: "GetChange",
+ Delay: 30,
+ MaxAttempts: 60,
+ Acceptors: []waiter.WaitAcceptor{
+ {
+ State: "success",
+ Matcher: "path",
+ Argument: "ChangeInfo.Status",
+ Expected: "INSYNC",
+ },
+ },
+ }
+
+ w := waiter.Waiter{
+ Client: c,
+ Input: input,
+ Config: waiterCfg,
+ }
+ return w.Wait()
+}
diff --git a/vendor/github.com/aws/aws-sdk-go/service/sqs/api.go b/vendor/github.com/aws/aws-sdk-go/service/sqs/api.go
index 8671d162728c..103abb3c8016 100644
--- a/vendor/github.com/aws/aws-sdk-go/service/sqs/api.go
+++ b/vendor/github.com/aws/aws-sdk-go/service/sqs/api.go
@@ -47,9 +47,7 @@ func (c *SQS) AddPermissionRequest(input *AddPermissionInput) (req *request.Requ
//
// Some API actions take lists of parameters. These lists are specified using
// the param.n notation. Values of n are integers starting from 1. For example,
-// a parameter list with two elements looks like this: &Attribute.1=this
-//
-// &Attribute.2=that
+// a parameter list with two elements looks like this:
func (c *SQS) AddPermission(input *AddPermissionInput) (*AddPermissionOutput, error) {
req, out := c.AddPermissionRequest(input)
err := req.Send()
@@ -145,9 +143,7 @@ func (c *SQS) ChangeMessageVisibilityBatchRequest(input *ChangeMessageVisibility
// returns an HTTP status code of 200. Some API actions take lists of parameters.
// These lists are specified using the param.n notation. Values of n are integers
// starting from 1. For example, a parameter list with two elements looks like
-// this: &Attribute.1=this
-//
-// &Attribute.2=that
+// this:
func (c *SQS) ChangeMessageVisibilityBatch(input *ChangeMessageVisibilityBatchInput) (*ChangeMessageVisibilityBatchOutput, error) {
req, out := c.ChangeMessageVisibilityBatchRequest(input)
err := req.Send()
@@ -196,9 +192,7 @@ func (c *SQS) CreateQueueRequest(input *CreateQueueInput) (req *request.Request,
//
// Some API actions take lists of parameters. These lists are specified using
// the param.n notation. Values of n are integers starting from 1. For example,
-// a parameter list with two elements looks like this: &Attribute.1=this
-//
-// &Attribute.2=that
+// a parameter list with two elements looks like this:
func (c *SQS) CreateQueue(input *CreateQueueInput) (*CreateQueueOutput, error) {
req, out := c.CreateQueueRequest(input)
err := req.Send()
@@ -283,9 +277,7 @@ func (c *SQS) DeleteMessageBatchRequest(input *DeleteMessageBatchInput) (req *re
//
// Some API actions take lists of parameters. These lists are specified using
// the param.n notation. Values of n are integers starting from 1. For example,
-// a parameter list with two elements looks like this: &Attribute.1=this
-//
-// &Attribute.2=that
+// a parameter list with two elements looks like this:
func (c *SQS) DeleteMessageBatch(input *DeleteMessageBatchInput) (*DeleteMessageBatchOutput, error) {
req, out := c.DeleteMessageBatchRequest(input)
err := req.Send()
@@ -358,27 +350,27 @@ func (c *SQS) GetQueueAttributesRequest(input *GetQueueAttributesInput) (req *re
}
// Gets attributes for the specified queue. The following attributes are supported:
-// All - returns all values. ApproximateNumberOfMessages - returns the approximate
+// All - returns all values. ApproximateNumberOfMessages - returns the approximate
// number of visible messages in a queue. For more information, see Resources
// Required to Process Messages (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/ApproximateNumber.html)
-// in the Amazon SQS Developer Guide. ApproximateNumberOfMessagesNotVisible
+// in the Amazon SQS Developer Guide. ApproximateNumberOfMessagesNotVisible
// - returns the approximate number of messages that are not timed-out and not
// deleted. For more information, see Resources Required to Process Messages
// (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/ApproximateNumber.html)
-// in the Amazon SQS Developer Guide. VisibilityTimeout - returns the visibility
+// in the Amazon SQS Developer Guide. VisibilityTimeout - returns the visibility
// timeout for the queue. For more information about visibility timeout, see
// Visibility Timeout (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html)
-// in the Amazon SQS Developer Guide. CreatedTimestamp - returns the time when
-// the queue was created (epoch time in seconds). LastModifiedTimestamp - returns
-// the time when the queue was last changed (epoch time in seconds). Policy
-// - returns the queue's policy. MaximumMessageSize - returns the limit of
-// how many bytes a message can contain before Amazon SQS rejects it. MessageRetentionPeriod
-// - returns the number of seconds Amazon SQS retains a message. QueueArn -
-// returns the queue's Amazon resource name (ARN). ApproximateNumberOfMessagesDelayed
+// in the Amazon SQS Developer Guide. CreatedTimestamp - returns the time when
+// the queue was created (epoch time in seconds). LastModifiedTimestamp - returns
+// the time when the queue was last changed (epoch time in seconds). Policy
+// - returns the queue's policy. MaximumMessageSize - returns the limit of how
+// many bytes a message can contain before Amazon SQS rejects it. MessageRetentionPeriod
+// - returns the number of seconds Amazon SQS retains a message. QueueArn -
+// returns the queue's Amazon resource name (ARN). ApproximateNumberOfMessagesDelayed
// - returns the approximate number of messages that are pending to be added
-// to the queue. DelaySeconds - returns the default delay on the queue in seconds.
-// ReceiveMessageWaitTimeSeconds - returns the time for which a ReceiveMessage
-// call will wait for a message to arrive. RedrivePolicy - returns the parameters
+// to the queue. DelaySeconds - returns the default delay on the queue in seconds.
+// ReceiveMessageWaitTimeSeconds - returns the time for which a ReceiveMessage
+// call will wait for a message to arrive. RedrivePolicy - returns the parameters
// for dead letter queue functionality of the source queue. For more information
// about RedrivePolicy and dead letter queues, see Using Amazon SQS Dead Letter
// Queues (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/SQSDeadLetterQueue.html)
@@ -389,9 +381,7 @@ func (c *SQS) GetQueueAttributesRequest(input *GetQueueAttributesInput) (req *re
// handle new attributes gracefully. Some API actions take lists of parameters.
// These lists are specified using the param.n notation. Values of n are integers
// starting from 1. For example, a parameter list with two elements looks like
-// this: &Attribute.1=this
-//
-// &Attribute.2=that
+// this:
func (c *SQS) GetQueueAttributes(input *GetQueueAttributesInput) (*GetQueueAttributesOutput, error) {
req, out := c.GetQueueAttributesRequest(input)
err := req.Send()
@@ -708,9 +698,7 @@ func (c *SQS) SendMessageBatchRequest(input *SendMessageBatchInput) (req *reques
// returns an HTTP status code of 200. Some API actions take lists of parameters.
// These lists are specified using the param.n notation. Values of n are integers
// starting from 1. For example, a parameter list with two elements looks like
-// this: &Attribute.1=this
-//
-// &Attribute.2=that
+// this:
func (c *SQS) SendMessageBatch(input *SendMessageBatchInput) (*SendMessageBatchOutput, error) {
req, out := c.SendMessageBatchRequest(input)
err := req.Send()
@@ -886,11 +874,9 @@ func (s ChangeMessageVisibilityBatchOutput) GoString() string {
// starting with 1. For example, a parameter list for this action might look
// like this:
//
-// &ChangeMessageVisibilityBatchRequestEntry.1.Id=change_visibility_msg_2
//
-// &ChangeMessageVisibilityBatchRequestEntry.1.ReceiptHandle=Your_Receipt_Handle
//
-// &ChangeMessageVisibilityBatchRequestEntry.1.VisibilityTimeout=45
+// Your_Receipt_Handle]]>
type ChangeMessageVisibilityBatchRequestEntry struct {
_ struct{} `type:"structure"`
@@ -981,19 +967,19 @@ type CreateQueueInput struct {
// The following lists the names, descriptions, and values of the special request
// parameters the CreateQueue action uses:
//
- // DelaySeconds - The time in seconds that the delivery of all messages
- // in the queue will be delayed. An integer from 0 to 900 (15 minutes). The
- // default for this attribute is 0 (zero). MaximumMessageSize - The limit of
- // how many bytes a message can contain before Amazon SQS rejects it. An integer
- // from 1024 bytes (1 KiB) up to 262144 bytes (256 KiB). The default for this
- // attribute is 262144 (256 KiB). MessageRetentionPeriod - The number of seconds
- // Amazon SQS retains a message. Integer representing seconds, from 60 (1 minute)
- // to 1209600 (14 days). The default for this attribute is 345600 (4 days).
- // Policy - The queue's policy. A valid AWS policy. For more information about
- // policy structure, see Overview of AWS IAM Policies (http://docs.aws.amazon.com/IAM/latest/UserGuide/PoliciesOverview.html)
- // in the Amazon IAM User Guide. ReceiveMessageWaitTimeSeconds - The time for
+ // DelaySeconds - The time in seconds that the delivery of all messages in
+ // the queue will be delayed. An integer from 0 to 900 (15 minutes). The default
+ // for this attribute is 0 (zero). MaximumMessageSize - The limit of how many
+ // bytes a message can contain before Amazon SQS rejects it. An integer from
+ // 1024 bytes (1 KiB) up to 262144 bytes (256 KiB). The default for this attribute
+ // is 262144 (256 KiB). MessageRetentionPeriod - The number of seconds Amazon
+ // SQS retains a message. Integer representing seconds, from 60 (1 minute) to
+ // 1209600 (14 days). The default for this attribute is 345600 (4 days). Policy
+ // - The queue's policy. A valid AWS policy. For more information about policy
+ // structure, see Overview of AWS IAM Policies (http://docs.aws.amazon.com/IAM/latest/UserGuide/PoliciesOverview.html)
+ // in the Amazon IAM User Guide. ReceiveMessageWaitTimeSeconds - The time for
// which a ReceiveMessage call will wait for a message to arrive. An integer
- // from 0 to 20 (seconds). The default for this attribute is 0. VisibilityTimeout
+ // from 0 to 20 (seconds). The default for this attribute is 0. VisibilityTimeout
// - The visibility timeout for the queue. An integer from 0 to 43200 (12 hours).
// The default for this attribute is 30. For more information about visibility
// timeout, see Visibility Timeout (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html)
@@ -1460,13 +1446,13 @@ type ReceiveMessageInput struct {
// The following lists the names and descriptions of the attributes that can
// be returned:
//
- // All - returns all values. ApproximateFirstReceiveTimestamp - returns
- // the time when the message was first received from the queue (epoch time in
- // milliseconds). ApproximateReceiveCount - returns the number of times a message
- // has been received from the queue but not deleted. SenderId - returns the
- // AWS account number (or the IP address, if anonymous access is allowed) of
- // the sender. SentTimestamp - returns the time when the message was sent to
- // the queue (epoch time in milliseconds).
+ // All - returns all values. ApproximateFirstReceiveTimestamp - returns the
+ // time when the message was first received from the queue (epoch time in milliseconds).
+ // ApproximateReceiveCount - returns the number of times a message has been
+ // received from the queue but not deleted. SenderId - returns the AWS account
+ // number (or the IP address, if anonymous access is allowed) of the sender.
+ // SentTimestamp - returns the time when the message was sent to the queue (epoch
+ // time in milliseconds).
AttributeNames []*string `locationNameList:"AttributeName" type:"list" flattened:"true"`
// The maximum number of messages to return. Amazon SQS never returns more messages
@@ -1745,22 +1731,22 @@ type SetQueueAttributesInput struct {
// The following lists the names, descriptions, and values of the special request
// parameters the SetQueueAttributes action uses:
//
- // DelaySeconds - The time in seconds that the delivery of all messages
- // in the queue will be delayed. An integer from 0 to 900 (15 minutes). The
- // default for this attribute is 0 (zero). MaximumMessageSize - The limit of
- // how many bytes a message can contain before Amazon SQS rejects it. An integer
- // from 1024 bytes (1 KiB) up to 262144 bytes (256 KiB). The default for this
- // attribute is 262144 (256 KiB). MessageRetentionPeriod - The number of seconds
- // Amazon SQS retains a message. Integer representing seconds, from 60 (1 minute)
- // to 1209600 (14 days). The default for this attribute is 345600 (4 days).
- // Policy - The queue's policy. A valid AWS policy. For more information about
- // policy structure, see Overview of AWS IAM Policies (http://docs.aws.amazon.com/IAM/latest/UserGuide/PoliciesOverview.html)
- // in the Amazon IAM User Guide. ReceiveMessageWaitTimeSeconds - The time for
+ // DelaySeconds - The time in seconds that the delivery of all messages in
+ // the queue will be delayed. An integer from 0 to 900 (15 minutes). The default
+ // for this attribute is 0 (zero). MaximumMessageSize - The limit of how many
+ // bytes a message can contain before Amazon SQS rejects it. An integer from
+ // 1024 bytes (1 KiB) up to 262144 bytes (256 KiB). The default for this attribute
+ // is 262144 (256 KiB). MessageRetentionPeriod - The number of seconds Amazon
+ // SQS retains a message. Integer representing seconds, from 60 (1 minute) to
+ // 1209600 (14 days). The default for this attribute is 345600 (4 days). Policy
+ // - The queue's policy. A valid AWS policy. For more information about policy
+ // structure, see Overview of AWS IAM Policies (http://docs.aws.amazon.com/IAM/latest/UserGuide/PoliciesOverview.html)
+ // in the Amazon IAM User Guide. ReceiveMessageWaitTimeSeconds - The time for
// which a ReceiveMessage call will wait for a message to arrive. An integer
- // from 0 to 20 (seconds). The default for this attribute is 0. VisibilityTimeout
+ // from 0 to 20 (seconds). The default for this attribute is 0. VisibilityTimeout
// - The visibility timeout for the queue. An integer from 0 to 43200 (12 hours).
// The default for this attribute is 30. For more information about visibility
- // timeout, see Visibility Timeout in the Amazon SQS Developer Guide. RedrivePolicy
+ // timeout, see Visibility Timeout in the Amazon SQS Developer Guide. RedrivePolicy
// - The parameters for dead letter queue functionality of the source queue.
// For more information about RedrivePolicy and dead letter queues, see Using
// Amazon SQS Dead Letter Queues in the Amazon SQS Developer Guide.
diff --git a/website/source/docs/providers/aws/index.html.markdown b/website/source/docs/providers/aws/index.html.markdown
index 7bc328dad4cd..949c67f623ce 100644
--- a/website/source/docs/providers/aws/index.html.markdown
+++ b/website/source/docs/providers/aws/index.html.markdown
@@ -39,7 +39,7 @@ explained below:
- Static credentials
- Environment variables
- Shared credentials file
-
+- EC2 Role
### Static credentials ###
@@ -96,6 +96,21 @@ provider "aws" {
}
```
+###EC2 Role
+
+If you're running Terraform from an EC2 instance with IAM Instance Profile
+using IAM Role, Terraform will just ask
+[the metadata API](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#instance-metadata-security-credentials)
+endpoint for credentials.
+
+This is a preferred approach over any other when running in EC2 as you can avoid
+hardcoding credentials. Instead these are leased on-the-fly by Terraform
+which reduces the chance of leakage.
+
+You can provide custom metadata API endpoint via `AWS_METADATA_ENDPOINT` variable
+which expects the endpoint URL including the version
+and defaults to `http://169.254.169.254:80/latest`.
+
## Argument Reference
The following arguments are supported in the `provider` block:
@@ -156,4 +171,24 @@ Nested `endpoints` block supports the followings:
* `elb` - (Optional) Use this to override the default endpoint
URL constructed from the `region`. It's typically used to connect to
- custom elb endpoints.
\ No newline at end of file
+ custom elb endpoints.
+
+## Getting the Account ID
+
+If you use either `allowed_account_ids` or `forbidden_account_ids`,
+Terraform uses several approaches to get the actual account ID
+in order to compare it with allowed/forbidden ones.
+
+Approaches differ per auth providers:
+
+ * EC2 instance w/ IAM Instance Profile - [Metadata API](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)
+ is always used
+ * All other providers (ENV vars, shared creds file, ...)
+ will try two approaches in the following order
+ * `iam:GetUser` - typically useful for IAM Users. It also means
+ that each user needs to be privileged to call `iam:GetUser` for themselves.
+ * `iam:ListRoles` - this is specifically useful for IdP-federated profiles
+ which cannot use `iam:GetUser`. It also means that each federated user
+ need to be _assuming_ an IAM role which allows `iam:ListRoles`.
+ There is currently no better clean way to get account ID
+ out of the API when using federated account unfortunately.