Skip to content

Commit

Permalink
service/cloudformation: New Resource and Data Source: aws_cloudformat…
Browse files Browse the repository at this point in the history
…ion_type (hashicorp#18579)

* service/cloudformation: New Resource and Data Source: aws_cloudformation_type

Reference: hashicorp#12583

Please note: The handler binary is not included to prevent adding a large file to the repository. The test skip messaging includes directions how to generate it locally, if desired.

Output from acceptance testing in AWS Commercial:

```
--- PASS: TestAccAwsCloudformationType_basic (145.29s)
--- PASS: TestAccAwsCloudformationType_disappears (160.15s)
--- PASS: TestAccAwsCloudformationType_ExecutionRoleArn (145.92s)
--- PASS: TestAccAwsCloudformationType_LoggingConfig (147.60s)

--- PASS: TestAccAwsCloudformationTypeDataSource_Arn_Private (146.49s)
--- PASS: TestAccAwsCloudformationTypeDataSource_Arn_Public (12.57s)
--- PASS: TestAccAwsCloudformationTypeDataSource_TypeName_Private (144.70s)
--- PASS: TestAccAwsCloudformationTypeDataSource_TypeName_Public (12.46s)
```

Output from acceptance testing in AWS GovCloud (US):

```
--- PASS: TestAccAwsCloudformationType_basic (157.19s)
--- PASS: TestAccAwsCloudformationType_disappears (147.02s)
--- PASS: TestAccAwsCloudformationType_ExecutionRoleArn (146.99s)
--- PASS: TestAccAwsCloudformationType_LoggingConfig (159.26s)

--- PASS: TestAccAwsCloudformationTypeDataSource_Arn_Private (147.32s)
--- PASS: TestAccAwsCloudformationTypeDataSource_Arn_Public (18.42s)
--- PASS: TestAccAwsCloudformationTypeDataSource_TypeName_Private (147.18s)
--- PASS: TestAccAwsCloudformationTypeDataSource_TypeName_Public (18.35s)
```

* Update CHANGELOG for 18579

* docs/resource/aws_cloudformation_type: Fix typo

* tests/data-source/aws_cloudformation_type: Fix AWSAT005 report

Output from acceptance testing:

```
--- PASS: TestAccAwsCloudformationTypeDataSource_Arn_Public (11.22s)
```

* Apply suggestions from code review

Co-authored-by: Kit Ewbank <[email protected]>

Co-authored-by: Kit Ewbank <[email protected]>
  • Loading branch information
bflad and ewbankkit authored Apr 20, 2021
1 parent 93d578a commit a803491
Show file tree
Hide file tree
Showing 32 changed files with 2,322 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .changelog/18579.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-resource
aws_cloudformation_type
```

```release-note:new-data-source
aws_cloudformation_type
```
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ log.txt
markdown-link-check*.txt
changelog.tmp
terraform-provider-aws
aws/testdata/service/cloudformation/examplecompany-exampleservice-exampleresource/bin/handler

# Test exclusions
!command/test-fixtures/**/*.tfstate
Expand Down
163 changes: 163 additions & 0 deletions aws/data_source_aws_cloudformation_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package aws

import (
"context"
"fmt"
"regexp"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func dataSourceAwsCloudFormationType() *schema.Resource {
return &schema.Resource{
ReadWithoutTimeout: dataSourceAwsCloudFormationTypeRead,

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"default_version_id": {
Type: schema.TypeString,
Computed: true,
},
"deprecated_status": {
Type: schema.TypeString,
Computed: true,
},
"description": {
Type: schema.TypeString,
Computed: true,
},
"documentation_url": {
Type: schema.TypeString,
Computed: true,
},
"execution_role_arn": {
Type: schema.TypeString,
Computed: true,
},
"is_default_version": {
Type: schema.TypeBool,
Computed: true,
},
"logging_config": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"log_group_name": {
Type: schema.TypeString,
Computed: true,
},
"log_role_arn": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"provisioning_type": {
Type: schema.TypeString,
Computed: true,
},
"schema": {
Type: schema.TypeString,
Computed: true,
},
"source_url": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice(cloudformation.RegistryType_Values(), false),
},
"type_arn": {
Type: schema.TypeString,
Computed: true,
},
"type_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.All(
validation.StringLenBetween(10, 204),
validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9]{2,64}::[A-Za-z0-9]{2,64}::[A-Za-z0-9]{2,64}(::MODULE){0,1}`), "three alphanumeric character sections separated by double colons (::)"),
),
},
"version_id": {
Type: schema.TypeString,
Optional: true,
},
"visibility": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func dataSourceAwsCloudFormationTypeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).cfconn

input := &cloudformation.DescribeTypeInput{}

if v, ok := d.GetOk("arn"); ok {
input.Arn = aws.String(v.(string))
}

if v, ok := d.GetOk("type"); ok {
input.Type = aws.String(v.(string))
}

if v, ok := d.GetOk("type_name"); ok {
input.TypeName = aws.String(v.(string))
}

if v, ok := d.GetOk("version_id"); ok {
input.VersionId = aws.String(v.(string))
}

output, err := conn.DescribeTypeWithContext(ctx, input)

if err != nil {
return diag.FromErr(fmt.Errorf("error reading CloudFormation Type: %w", err))
}

if output == nil {
return diag.FromErr(fmt.Errorf("error reading CloudFormation Type: empty response"))
}

d.SetId(aws.StringValue(output.Arn))

d.Set("arn", output.Arn)
d.Set("default_version_id", output.DefaultVersionId)
d.Set("deprecated_status", output.DeprecatedStatus)
d.Set("description", output.Description)
d.Set("documentation_url", output.DocumentationUrl)
d.Set("execution_role_arn", output.ExecutionRoleArn)
d.Set("is_default_version", output.IsDefaultVersion)
if output.LoggingConfig != nil {
if err := d.Set("logging_config", []interface{}{flattenCloudformationLoggingConfig(output.LoggingConfig)}); err != nil {
return diag.FromErr(fmt.Errorf("error setting logging_config: %w", err))
}
} else {
d.Set("logging_config", nil)
}
d.Set("provisioning_type", output.ProvisioningType)
d.Set("schema", output.Schema)
d.Set("source_url", output.SourceUrl)
d.Set("type", output.Type)
d.Set("type_name", output.TypeName)
d.Set("visibility", output.Visibility)

return nil
}
206 changes: 206 additions & 0 deletions aws/data_source_aws_cloudformation_type_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package aws

import (
"fmt"
"regexp"
"testing"

"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccAwsCloudformationTypeDataSource_Arn_Private(t *testing.T) {
rName := acctest.RandomWithPrefix("tf-acc-test")
typeName := fmt.Sprintf("HashiCorp::TerraformAwsProvider::TfAccTest%s", acctest.RandString(8))
zipPath := testAccAwsCloudformationTypeZipGenerator(t, typeName)
resourceName := "aws_cloudformation_type.test"
dataSourceName := "data.aws_cloudformation_type.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, cloudformation.EndpointsID),
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckAwsCloudformationTypeDestroy,
Steps: []resource.TestStep{
{
Config: testAccAwsCloudformationTypeDataSourceConfigArnPrivate(rName, zipPath, typeName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "deprecated_status", resourceName, "deprecated_status"),
resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName, "description"),
resource.TestCheckResourceAttrPair(dataSourceName, "documentation_url", resourceName, "documentation_url"),
resource.TestCheckResourceAttrPair(dataSourceName, "execution_role_arn", resourceName, "execution_role_arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "is_default_version", resourceName, "is_default_version"),
resource.TestCheckResourceAttrPair(dataSourceName, "logging_config.#", resourceName, "logging_config.#"),
resource.TestCheckResourceAttrPair(dataSourceName, "provisioning_type", resourceName, "provisioning_type"),
resource.TestCheckResourceAttrPair(dataSourceName, "schema", resourceName, "schema"),
resource.TestCheckResourceAttrPair(dataSourceName, "source_url", resourceName, "source_url"),
resource.TestCheckResourceAttrPair(dataSourceName, "type", resourceName, "type"),
resource.TestCheckResourceAttrPair(dataSourceName, "type_name", resourceName, "type_name"),
resource.TestCheckResourceAttrPair(dataSourceName, "visibility", resourceName, "visibility"),
),
},
},
})
}

func TestAccAwsCloudformationTypeDataSource_Arn_Public(t *testing.T) {
dataSourceName := "data.aws_cloudformation_type.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, cloudformation.EndpointsID),
ProviderFactories: testAccProviderFactories,
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: testAccAwsCloudformationTypeDataSourceConfigArnPublic(),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckResourceAttrRegionalARNNoAccount(dataSourceName, "arn", "cloudformation", "type/resource/AWS-Athena-WorkGroup"),
resource.TestCheckResourceAttr(dataSourceName, "deprecated_status", cloudformation.DeprecatedStatusLive),
resource.TestMatchResourceAttr(dataSourceName, "description", regexp.MustCompile(`.*`)),
resource.TestCheckResourceAttr(dataSourceName, "documentation_url", ""),
resource.TestCheckResourceAttr(dataSourceName, "is_default_version", "true"),
resource.TestCheckResourceAttr(dataSourceName, "logging_config.#", "0"),
resource.TestCheckResourceAttr(dataSourceName, "provisioning_type", cloudformation.ProvisioningTypeFullyMutable),
resource.TestMatchResourceAttr(dataSourceName, "schema", regexp.MustCompile(`^\{.*`)),
resource.TestMatchResourceAttr(dataSourceName, "source_url", regexp.MustCompile(`^https://.+`)),
resource.TestCheckResourceAttr(dataSourceName, "type", cloudformation.RegistryTypeResource),
resource.TestCheckResourceAttr(dataSourceName, "type_name", "AWS::Athena::WorkGroup"),
resource.TestCheckResourceAttr(dataSourceName, "visibility", cloudformation.VisibilityPublic),
),
},
},
})
}

func TestAccAwsCloudformationTypeDataSource_TypeName_Private(t *testing.T) {
rName := acctest.RandomWithPrefix("tf-acc-test")
typeName := fmt.Sprintf("HashiCorp::TerraformAwsProvider::TfAccTest%s", acctest.RandString(8))
zipPath := testAccAwsCloudformationTypeZipGenerator(t, typeName)
resourceName := "aws_cloudformation_type.test"
dataSourceName := "data.aws_cloudformation_type.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, cloudformation.EndpointsID),
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckAwsCloudformationTypeDestroy,
Steps: []resource.TestStep{
{
Config: testAccAwsCloudformationTypeDataSourceConfigTypeNamePrivate(rName, zipPath, typeName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "deprecated_status", resourceName, "deprecated_status"),
resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName, "description"),
resource.TestCheckResourceAttrPair(dataSourceName, "documentation_url", resourceName, "documentation_url"),
resource.TestCheckResourceAttrPair(dataSourceName, "execution_role_arn", resourceName, "execution_role_arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "is_default_version", resourceName, "is_default_version"),
resource.TestCheckResourceAttrPair(dataSourceName, "logging_config.#", resourceName, "logging_config.#"),
resource.TestCheckResourceAttrPair(dataSourceName, "provisioning_type", resourceName, "provisioning_type"),
resource.TestCheckResourceAttrPair(dataSourceName, "schema", resourceName, "schema"),
resource.TestCheckResourceAttrPair(dataSourceName, "source_url", resourceName, "source_url"),
resource.TestCheckResourceAttrPair(dataSourceName, "type", resourceName, "type"),
resource.TestCheckResourceAttrPair(dataSourceName, "type_name", resourceName, "type_name"),
resource.TestCheckResourceAttrPair(dataSourceName, "visibility", resourceName, "visibility"),
),
},
},
})
}

func TestAccAwsCloudformationTypeDataSource_TypeName_Public(t *testing.T) {
dataSourceName := "data.aws_cloudformation_type.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, cloudformation.EndpointsID),
ProviderFactories: testAccProviderFactories,
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: testAccAwsCloudformationTypeDataSourceConfigTypeNamePublic(),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckResourceAttrRegionalARNNoAccount(dataSourceName, "arn", "cloudformation", "type/resource/AWS-Athena-WorkGroup"),
resource.TestCheckResourceAttr(dataSourceName, "deprecated_status", cloudformation.DeprecatedStatusLive),
resource.TestMatchResourceAttr(dataSourceName, "description", regexp.MustCompile(`.*`)),
resource.TestCheckResourceAttr(dataSourceName, "documentation_url", ""),
resource.TestCheckResourceAttr(dataSourceName, "is_default_version", "true"),
resource.TestCheckResourceAttr(dataSourceName, "logging_config.#", "0"),
resource.TestCheckResourceAttr(dataSourceName, "provisioning_type", cloudformation.ProvisioningTypeFullyMutable),
resource.TestMatchResourceAttr(dataSourceName, "schema", regexp.MustCompile(`^\{.*`)),
resource.TestMatchResourceAttr(dataSourceName, "source_url", regexp.MustCompile(`^https://.+`)),
resource.TestCheckResourceAttr(dataSourceName, "type", cloudformation.RegistryTypeResource),
resource.TestCheckResourceAttr(dataSourceName, "type_name", "AWS::Athena::WorkGroup"),
resource.TestCheckResourceAttr(dataSourceName, "visibility", cloudformation.VisibilityPublic),
),
},
},
})
}

func testAccCloudformationTypeConfigPrivateBase(rName string, zipPath string, typeName string) string {
return fmt.Sprintf(`
data "aws_partition" "current" {}
resource "aws_s3_bucket" "test" {
bucket = %[1]q
force_destroy = true
}
resource "aws_s3_bucket_object" "test" {
bucket = aws_s3_bucket.test.bucket
key = "test"
source = %[2]q
}
resource "aws_cloudformation_type" "test" {
schema_handler_package = "s3://${aws_s3_bucket_object.test.bucket}/${aws_s3_bucket_object.test.key}"
type = "RESOURCE"
type_name = %[3]q
}
`, rName, zipPath, typeName)
}

func testAccAwsCloudformationTypeDataSourceConfigArnPrivate(rName string, zipPath string, typeName string) string {
return composeConfig(
testAccCloudformationTypeConfigPrivateBase(rName, zipPath, typeName),
`
data "aws_cloudformation_type" "test" {
arn = aws_cloudformation_type.test.arn
}
`)
}

func testAccAwsCloudformationTypeDataSourceConfigArnPublic() string {
return `
data "aws_partition" "current" {}
data "aws_region" "current" {}
data "aws_cloudformation_type" "test" {
arn = "arn:${data.aws_partition.current.partition}:cloudformation:${data.aws_region.current.name}::type/resource/AWS-Athena-WorkGroup"
}
`
}

func testAccAwsCloudformationTypeDataSourceConfigTypeNamePrivate(rName string, zipPath string, typeName string) string {
return composeConfig(
testAccCloudformationTypeConfigPrivateBase(rName, zipPath, typeName),
`
data "aws_cloudformation_type" "test" {
type = aws_cloudformation_type.test.type
type_name = aws_cloudformation_type.test.type_name
}
`)
}

func testAccAwsCloudformationTypeDataSourceConfigTypeNamePublic() string {
return `
data "aws_cloudformation_type" "test" {
type = "RESOURCE"
type_name = "AWS::Athena::WorkGroup"
}
`
}
Loading

0 comments on commit a803491

Please sign in to comment.