provider/aws: Allow ARN identifier to be set

Allows users from govcloud and other regions (aws-cn) to now use the following resources correctly:

```
- data "aws_billing_service_account"
- data "aws_elb_service_account"
- resource "aws_cloudfront_origin_access_identity"
- resource "aws_ecs_service"
- resource "aws_iam_saml_provider"
- resource "aws_lambda_permission"
- resource "aws_sns_topic_policy"
```
This commit is contained in:
Jake Champlin 2017-01-23 11:30:28 -05:00
parent 4fcc301b83
commit 8159731c91
No known key found for this signature in database
GPG Key ID: DC31F41958EF4AC2
15 changed files with 69 additions and 51 deletions

View File

@ -1,6 +1,8 @@
package aws
import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
)
@ -12,7 +14,7 @@ func dataSourceAwsBillingServiceAccount() *schema.Resource {
Read: dataSourceAwsBillingServiceAccountRead,
Schema: map[string]*schema.Schema{
"arn": &schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
@ -23,7 +25,7 @@ func dataSourceAwsBillingServiceAccount() *schema.Resource {
func dataSourceAwsBillingServiceAccountRead(d *schema.ResourceData, meta interface{}) error {
d.SetId(billingAccountId)
d.Set("arn", "arn:aws:iam::"+billingAccountId+":root")
d.Set("arn", fmt.Sprintf("arn:%s:iam::%s:root", meta.(*AWSClient).partition, billingAccountId))
return nil
}

View File

@ -11,7 +11,7 @@ func TestAccAWSBillingServiceAccount_basic(t *testing.T) {
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccCheckAwsBillingServiceAccountConfig,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.aws_billing_service_account.main", "id", "386209384616"),

View File

@ -31,11 +31,11 @@ func dataSourceAwsElbServiceAccount() *schema.Resource {
Read: dataSourceAwsElbServiceAccountRead,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
"region": {
Type: schema.TypeString,
Optional: true,
},
"arn": &schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
@ -52,7 +52,7 @@ func dataSourceAwsElbServiceAccountRead(d *schema.ResourceData, meta interface{}
if accid, ok := elbAccountIdPerRegionMap[region]; ok {
d.SetId(accid)
d.Set("arn", "arn:aws:iam::"+accid+":root")
d.Set("arn", fmt.Sprintf("arn:%s:iam::%s:root", meta.(*AWSClient).partition, accid))
return nil
}

View File

@ -11,14 +11,14 @@ func TestAccAWSElbServiceAccount_basic(t *testing.T) {
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccCheckAwsElbServiceAccountConfig,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.aws_elb_service_account.main", "id", "797873946194"),
resource.TestCheckResourceAttr("data.aws_elb_service_account.main", "arn", "arn:aws:iam::797873946194:root"),
),
},
resource.TestStep{
{
Config: testAccCheckAwsElbServiceAccountExplicitRegionConfig,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.aws_elb_service_account.regional", "id", "156460612806"),

View File

@ -20,28 +20,28 @@ func resourceAwsCloudFrontOriginAccessIdentity() *schema.Resource {
},
Schema: map[string]*schema.Schema{
"comment": &schema.Schema{
"comment": {
Type: schema.TypeString,
Optional: true,
Default: "",
},
"caller_reference": &schema.Schema{
"caller_reference": {
Type: schema.TypeString,
Computed: true,
},
"cloudfront_access_identity_path": &schema.Schema{
"cloudfront_access_identity_path": {
Type: schema.TypeString,
Computed: true,
},
"etag": &schema.Schema{
"etag": {
Type: schema.TypeString,
Computed: true,
},
"iam_arn": &schema.Schema{
"iam_arn": {
Type: schema.TypeString,
Computed: true,
},
"s3_canonical_user_id": &schema.Schema{
"s3_canonical_user_id": {
Type: schema.TypeString,
Computed: true,
},
@ -81,7 +81,8 @@ func resourceAwsCloudFrontOriginAccessIdentityRead(d *schema.ResourceData, meta
d.Set("etag", resp.ETag)
d.Set("s3_canonical_user_id", resp.CloudFrontOriginAccessIdentity.S3CanonicalUserId)
d.Set("cloudfront_access_identity_path", fmt.Sprintf("origin-access-identity/cloudfront/%s", *resp.CloudFrontOriginAccessIdentity.Id))
d.Set("iam_arn", fmt.Sprintf("arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity %s", *resp.CloudFrontOriginAccessIdentity.Id))
d.Set("iam_arn", fmt.Sprintf("arn:%s:iam::cloudfront:user/CloudFront Origin Access Identity %s",
meta.(*AWSClient).partition, *resp.CloudFrontOriginAccessIdentity.Id))
return nil
}

View File

@ -17,7 +17,7 @@ func TestAccAWSCloudFrontOriginAccessIdentity_basic(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudFrontOriginAccessIdentityDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSCloudFrontOriginAccessIdentityConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudFrontOriginAccessIdentityExistence("aws_cloudfront_origin_access_identity.origin_access_identity"),
@ -46,7 +46,7 @@ func TestAccAWSCloudFrontOriginAccessIdentity_noComment(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudFrontOriginAccessIdentityDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSCloudFrontOriginAccessIdentityNoCommentConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudFrontOriginAccessIdentityExistence("aws_cloudfront_origin_access_identity.origin_access_identity"),

View File

@ -282,7 +282,7 @@ func resourceAwsEcsServiceRead(d *schema.ResourceData, meta interface{}) error {
d.Set("name", service.ServiceName)
// Save task definition in the same format
if strings.HasPrefix(d.Get("task_definition").(string), "arn:aws:ecs:") {
if regexp.MustCompile(`^arn:[\w-]+:ecs:`).MatchString(d.Get("task_definition").(string)) {
d.Set("task_definition", service.TaskDefinition)
} else {
taskDefinition := buildFamilyAndRevisionFromARN(*service.TaskDefinition)
@ -292,7 +292,7 @@ func resourceAwsEcsServiceRead(d *schema.ResourceData, meta interface{}) error {
d.Set("desired_count", service.DesiredCount)
// Save cluster in the same format
if strings.HasPrefix(d.Get("cluster").(string), "arn:aws:ecs:") {
if regexp.MustCompile(`^arn:[\w-]+:ecs:`).MatchString(d.Get("cluster").(string)) {
d.Set("cluster", service.ClusterArn)
} else {
clusterARN := getNameFromARN(*service.ClusterArn)
@ -301,7 +301,7 @@ func resourceAwsEcsServiceRead(d *schema.ResourceData, meta interface{}) error {
// Save IAM role in the same format
if service.RoleArn != nil {
if strings.HasPrefix(d.Get("iam_role").(string), "arn:aws:iam:") {
if regexp.MustCompile(`^arn:[\w-]+:iam:`).MatchString(d.Get("iam_role").(string)) {
d.Set("iam_role", service.RoleArn)
} else {
roleARN := getNameFromARN(*service.RoleArn)

View File

@ -23,20 +23,20 @@ func resourceAwsIamSamlProvider() *schema.Resource {
},
Schema: map[string]*schema.Schema{
"arn": &schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"valid_until": &schema.Schema{
"valid_until": {
Type: schema.TypeString,
Computed: true,
},
"name": &schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"saml_metadata_document": &schema.Schema{
"saml_metadata_document": {
Type: schema.TypeString,
Required: true,
},
@ -75,7 +75,7 @@ func resourceAwsIamSamlProviderRead(d *schema.ResourceData, meta interface{}) er
validUntil := out.ValidUntil.Format(time.RFC1123)
d.Set("arn", d.Id())
name, err := extractNameFromIAMSamlProviderArn(d.Id())
name, err := extractNameFromIAMSamlProviderArn(d.Id(), meta.(*AWSClient).partition)
if err != nil {
return err
}
@ -112,9 +112,9 @@ func resourceAwsIamSamlProviderDelete(d *schema.ResourceData, meta interface{})
return err
}
func extractNameFromIAMSamlProviderArn(arn string) (string, error) {
func extractNameFromIAMSamlProviderArn(arn, partition string) (string, error) {
// arn:aws:iam::123456789012:saml-provider/tf-salesforce-test
r := regexp.MustCompile("^arn:aws:iam::[0-9]{12}:saml-provider/(.+)$")
r := regexp.MustCompile(fmt.Sprintf("^arn:%s:iam::[0-9]{12}:saml-provider/(.+)$", partition))
submatches := r.FindStringSubmatch(arn)
if len(submatches) != 2 {
return "", fmt.Errorf("Unable to extract name from a given ARN: %q", arn)

View File

@ -17,13 +17,13 @@ func TestAccAWSIAMSamlProvider_basic(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckIAMSamlProviderDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccIAMSamlProviderConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckIAMSamlProvider("aws_iam_saml_provider.salesforce"),
),
},
resource.TestStep{
{
Config: testAccIAMSamlProviderConfigUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckIAMSamlProvider("aws_iam_saml_provider.salesforce"),

View File

@ -15,7 +15,7 @@ import (
"github.com/hashicorp/terraform/helper/schema"
)
var LambdaFunctionRegexp = `^(arn:aws:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`
var LambdaFunctionRegexp = `^(arn:[\w-]+:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`
func resourceAwsLambdaPermission() *schema.Resource {
return &schema.Resource{
@ -24,42 +24,42 @@ func resourceAwsLambdaPermission() *schema.Resource {
Delete: resourceAwsLambdaPermissionDelete,
Schema: map[string]*schema.Schema{
"action": &schema.Schema{
"action": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateLambdaPermissionAction,
},
"function_name": &schema.Schema{
"function_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateLambdaFunctionName,
},
"principal": &schema.Schema{
"principal": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"qualifier": &schema.Schema{
"qualifier": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateLambdaQualifier,
},
"source_account": &schema.Schema{
"source_account": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateAwsAccountId,
},
"source_arn": &schema.Schema{
"source_arn": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateArn,
},
"statement_id": &schema.Schema{
"statement_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
@ -216,7 +216,7 @@ func resourceAwsLambdaPermissionRead(d *schema.ResourceData, meta interface{}) e
}
// Save Lambda function name in the same format
if strings.HasPrefix(d.Get("function_name").(string), "arn:aws:lambda:") {
if regexp.MustCompile(`^arn:[\w-]+:lambda:`).MatchString(d.Get("function_name").(string)) {
// Strip qualifier off
trimmedArn := strings.TrimSuffix(statement.Resource, ":"+qualifier)
d.Set("function_name", trimmedArn)

View File

@ -63,6 +63,18 @@ func TestLambdaPermissionGetQualifierFromLambdaAliasOrVersionArn_alias(t *testin
}
}
func TestLambdaPermissionGetQualifierFromLambdaAliasOrVersionArn_govcloud(t *testing.T) {
arnWithAlias := "arn:aws-us-gov:lambda:us-west-2:187636751137:function:lambda_function_name:testalias"
expectedQualifier := "testalias"
qualifier, err := getQualifierFromLambdaAliasOrVersionArn(arnWithAlias)
if err != nil {
t.Fatalf("Expected no error when getting qualifier: %s", err)
}
if qualifier != expectedQualifier {
t.Fatalf("Expected qualifier to match (%q != %q)", qualifier, expectedQualifier)
}
}
func TestLambdaPermissionGetQualifierFromLambdaAliasOrVersionArn_version(t *testing.T) {
arnWithVersion := "arn:aws:lambda:us-west-2:187636751137:function:lambda_function_name:223"
expectedQualifier := "223"
@ -141,7 +153,7 @@ func TestAccAWSLambdaPermission_basic(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSLambdaPermissionConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.allow_cloudwatch", &statement),
@ -165,7 +177,7 @@ func TestAccAWSLambdaPermission_withRawFunctionName(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSLambdaPermissionConfig_withRawFunctionName,
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.with_raw_func_name", &statement),
@ -188,7 +200,7 @@ func TestAccAWSLambdaPermission_withQualifier(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSLambdaPermissionConfig_withQualifier,
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.with_qualifier", &statement),
@ -217,7 +229,7 @@ func TestAccAWSLambdaPermission_multiplePerms(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSLambdaPermissionConfig_multiplePerms,
Check: resource.ComposeTestCheckFunc(
// 1st
@ -236,7 +248,7 @@ func TestAccAWSLambdaPermission_multiplePerms(t *testing.T) {
regexp.MustCompile(":function:lambda_function_name_perm_multiperms$")),
),
},
resource.TestStep{
{
Config: testAccAWSLambdaPermissionConfig_multiplePermsModified,
Check: resource.ComposeTestCheckFunc(
// 1st
@ -277,7 +289,7 @@ func TestAccAWSLambdaPermission_withS3(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: fmt.Sprintf(testAccAWSLambdaPermissionConfig_withS3_tpl, rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.with_s3", &statement),
@ -303,7 +315,7 @@ func TestAccAWSLambdaPermission_withSNS(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSLambdaPermissionConfig_withSNS,
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.with_sns", &statement),

View File

@ -102,7 +102,7 @@ func resourceAwsSnsTopicPolicyRead(d *schema.ResourceData, meta interface{}) err
}
func resourceAwsSnsTopicPolicyDelete(d *schema.ResourceData, meta interface{}) error {
accountId, err := getAccountIdFromSnsTopicArn(d.Id())
accountId, err := getAccountIdFromSnsTopicArn(d.Id(), meta.(*AWSClient).partition)
if err != nil {
return err
}
@ -134,9 +134,10 @@ func resourceAwsSnsTopicPolicyDelete(d *schema.ResourceData, meta interface{}) e
return nil
}
func getAccountIdFromSnsTopicArn(arn string) (string, error) {
func getAccountIdFromSnsTopicArn(arn, partition string) (string, error) {
// arn:aws:sns:us-west-2:123456789012:test-new
re := regexp.MustCompile("^arn:aws:sns:[^:]+:([0-9]{12}):.+")
// arn:aws-us-gov:sns:us-west-2:123456789012:test-new
re := regexp.MustCompile(fmt.Sprintf("^arn:%s:sns:[^:]+:([0-9]{12}):.+", partition))
matches := re.FindStringSubmatch(arn)
if len(matches) != 2 {
return "", fmt.Errorf("Unable to get account ID from ARN (%q)", arn)

View File

@ -13,7 +13,7 @@ func TestAccAWSSNSTopicPolicy_basic(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSNSTopicDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSSNSTopicConfig_withPolicy,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSNSTopicExists("aws_sns_topic.test"),

View File

@ -234,7 +234,7 @@ func validateLambdaFunctionName(v interface{}, k string) (ws []string, errors []
"%q cannot be longer than 140 characters: %q", k, value))
}
// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
pattern := `^(arn:aws:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`
pattern := `^(arn:[\w-]+:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`
if !regexp.MustCompile(pattern).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q doesn't comply with restrictions (%q): %q",
@ -297,7 +297,7 @@ func validateArn(v interface{}, k string) (ws []string, errors []error) {
}
// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
pattern := `^arn:aws:([a-zA-Z0-9\-])+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:(.*)$`
pattern := `^arn:[\w-]+:([a-zA-Z0-9\-])+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:(.*)$`
if !regexp.MustCompile(pattern).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q doesn't look like a valid ARN (%q): %q",

View File

@ -78,6 +78,7 @@ func TestValidateCloudWatchEventRuleName(t *testing.T) {
func TestValidateLambdaFunctionName(t *testing.T) {
validNames := []string{
"arn:aws:lambda:us-west-2:123456789012:function:ThumbNail",
"arn:aws-us-gov:lambda:us-west-2:123456789012:function:ThumbNail",
"FunctionName",
"function-name",
}
@ -203,6 +204,7 @@ func TestValidateArn(t *testing.T) {
"arn:aws:events:us-east-1:319201112229:rule/rule_name", // CloudWatch Rule
"arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction", // Lambda function
"arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction:Qualifier", // Lambda func qualifier
"arn:aws-us-gov:s3:::corp_bucket/object.png", // GovCloud ARN
}
for _, v := range validNames {
_, errors := validateArn(v, "arn")