From 29c750afca2bd8207cc7b49a601baa282dc3313a Mon Sep 17 00:00:00 2001 From: Shuhei Kitagawa Date: Mon, 5 Apr 2021 20:53:37 +0900 Subject: [PATCH] resource/aws_cloudfront_key_group - new resource (#17041) Output from acceptance testing in AWS Commercial: ``` --- PASS: TestAccAWSCloudFrontKeyGroup_disappears (10.83s) --- PASS: TestAccAWSCloudFrontKeyGroup_basic (12.39s) --- PASS: TestAccAWSCloudFrontKeyGroup_Comment (19.26s) --- PASS: TestAccAWSCloudFrontKeyGroup_Items (19.63s) ``` Output from acceptance testing in AWS GovCloud (US): ``` --- SKIP: TestAccAWSCloudFrontKeyGroup_basic (1.33s) --- SKIP: TestAccAWSCloudFrontKeyGroup_disappears (1.33s) --- SKIP: TestAccAWSCloudFrontKeyGroup_Comment (1.33s) --- SKIP: TestAccAWSCloudFrontKeyGroup_Items (1.33s) ``` --- .changelog/17041.txt | 3 + aws/provider.go | 1 + aws/resource_aws_cloudfront_key_group.go | 144 +++++++++ aws/resource_aws_cloudfront_key_group_test.go | 286 ++++++++++++++++++ ...s_cloudfront_origin_request_policy_test.go | 2 +- .../docs/r/cloudfront_key_group.html.markdown | 42 +++ 6 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 .changelog/17041.txt create mode 100644 aws/resource_aws_cloudfront_key_group.go create mode 100644 aws/resource_aws_cloudfront_key_group_test.go create mode 100644 website/docs/r/cloudfront_key_group.html.markdown diff --git a/.changelog/17041.txt b/.changelog/17041.txt new file mode 100644 index 000000000000..84097156d63c --- /dev/null +++ b/.changelog/17041.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_cloudfront_key_group +``` diff --git a/aws/provider.go b/aws/provider.go index 9a268bf1f49a..ca5fa0badb59 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -515,6 +515,7 @@ func Provider() *schema.Provider { "aws_cloudformation_stack_set_instance": resourceAwsCloudFormationStackSetInstance(), "aws_cloudfront_cache_policy": resourceAwsCloudFrontCachePolicy(), "aws_cloudfront_distribution": resourceAwsCloudFrontDistribution(), + "aws_cloudfront_key_group": resourceAwsCloudFrontKeyGroup(), "aws_cloudfront_origin_access_identity": resourceAwsCloudFrontOriginAccessIdentity(), "aws_cloudfront_origin_request_policy": resourceAwsCloudFrontOriginRequestPolicy(), "aws_cloudfront_public_key": resourceAwsCloudFrontPublicKey(), diff --git a/aws/resource_aws_cloudfront_key_group.go b/aws/resource_aws_cloudfront_key_group.go new file mode 100644 index 000000000000..fea7df8b68b2 --- /dev/null +++ b/aws/resource_aws_cloudfront_key_group.go @@ -0,0 +1,144 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudfront" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAwsCloudFrontKeyGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsCloudFrontKeyGroupCreate, + Read: resourceAwsCloudFrontKeyGroupRead, + Update: resourceAwsCloudFrontKeyGroupUpdate, + Delete: resourceAwsCloudFrontKeyGroupDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "comment": { + Type: schema.TypeString, + Optional: true, + }, + "etag": { + Type: schema.TypeString, + Computed: true, + }, + "items": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceAwsCloudFrontKeyGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudfrontconn + + input := &cloudfront.CreateKeyGroupInput{ + KeyGroupConfig: expandCloudFrontKeyGroupConfig(d), + } + + log.Println("[DEBUG] Create CloudFront Key Group:", input) + + output, err := conn.CreateKeyGroup(input) + if err != nil { + return fmt.Errorf("error creating CloudFront Key Group: %w", err) + } + + if output == nil || output.KeyGroup == nil { + return fmt.Errorf("error creating CloudFront Key Group: empty response") + } + + d.SetId(aws.StringValue(output.KeyGroup.Id)) + return resourceAwsCloudFrontKeyGroupRead(d, meta) +} + +func resourceAwsCloudFrontKeyGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudfrontconn + input := &cloudfront.GetKeyGroupInput{ + Id: aws.String(d.Id()), + } + + output, err := conn.GetKeyGroup(input) + if err != nil { + if !d.IsNewResource() && isAWSErr(err, cloudfront.ErrCodeNoSuchResource, "") { + log.Printf("[WARN] No key group found: %s, removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("error reading CloudFront Key Group (%s): %w", d.Id(), err) + } + + if output == nil || output.KeyGroup == nil || output.KeyGroup.KeyGroupConfig == nil { + return fmt.Errorf("error reading CloudFront Key Group: empty response") + } + + keyGroupConfig := output.KeyGroup.KeyGroupConfig + + d.Set("name", keyGroupConfig.Name) + d.Set("comment", keyGroupConfig.Comment) + d.Set("items", flattenStringSet(keyGroupConfig.Items)) + d.Set("etag", output.ETag) + + return nil +} + +func resourceAwsCloudFrontKeyGroupUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudfrontconn + + input := &cloudfront.UpdateKeyGroupInput{ + Id: aws.String(d.Id()), + KeyGroupConfig: expandCloudFrontKeyGroupConfig(d), + IfMatch: aws.String(d.Get("etag").(string)), + } + + _, err := conn.UpdateKeyGroup(input) + if err != nil { + return fmt.Errorf("error updating CloudFront Key Group (%s): %w", d.Id(), err) + } + + return resourceAwsCloudFrontKeyGroupRead(d, meta) +} + +func resourceAwsCloudFrontKeyGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudfrontconn + + input := &cloudfront.DeleteKeyGroupInput{ + Id: aws.String(d.Id()), + IfMatch: aws.String(d.Get("etag").(string)), + } + + _, err := conn.DeleteKeyGroup(input) + if err != nil { + if isAWSErr(err, cloudfront.ErrCodeNoSuchResource, "") { + return nil + } + return fmt.Errorf("error deleting CloudFront Key Group (%s): %w", d.Id(), err) + } + + return nil +} + +func expandCloudFrontKeyGroupConfig(d *schema.ResourceData) *cloudfront.KeyGroupConfig { + keyGroupConfig := &cloudfront.KeyGroupConfig{ + Items: expandStringSet(d.Get("items").(*schema.Set)), + Name: aws.String(d.Get("name").(string)), + } + + if v, ok := d.GetOk("comment"); ok { + keyGroupConfig.Comment = aws.String(v.(string)) + } + + return keyGroupConfig +} diff --git a/aws/resource_aws_cloudfront_key_group_test.go b/aws/resource_aws_cloudfront_key_group_test.go new file mode 100644 index 000000000000..0bcb9e8f728e --- /dev/null +++ b/aws/resource_aws_cloudfront_key_group_test.go @@ -0,0 +1,286 @@ +package aws + +import ( + "fmt" + "log" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudfront" + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func init() { + resource.AddTestSweepers("aws_cloudfront_key_group", &resource.Sweeper{ + Name: "aws_cloudfront_key_group", + F: testSweepCloudFrontKeyGroup, + }) +} + +func testSweepCloudFrontKeyGroup(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("Error getting client: %w", err) + } + conn := client.(*AWSClient).cloudfrontconn + var sweeperErrs *multierror.Error + + input := &cloudfront.ListKeyGroupsInput{} + + for { + output, err := conn.ListKeyGroups(input) + if err != nil { + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping CloudFront key group sweep for %s: %s", region, err) + return nil + } + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving CloudFront key group: %w", err)) + return sweeperErrs.ErrorOrNil() + } + + if output == nil || output.KeyGroupList == nil || len(output.KeyGroupList.Items) == 0 { + log.Print("[DEBUG] No CloudFront key group to sweep") + return nil + } + + for _, item := range output.KeyGroupList.Items { + strId := aws.StringValue(item.KeyGroup.Id) + log.Printf("[INFO] CloudFront key group %s", strId) + _, err := conn.DeleteKeyGroup(&cloudfront.DeleteKeyGroupInput{ + Id: item.KeyGroup.Id, + }) + if err != nil { + sweeperErr := fmt.Errorf("error deleting CloudFront key group %s: %w", strId, err) + log.Printf("[ERROR] %s", sweeperErr) + sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) + continue + } + } + + if output.KeyGroupList.NextMarker == nil { + break + } + input.Marker = output.KeyGroupList.NextMarker + } + + return sweeperErrs.ErrorOrNil() +} + +func TestAccAWSCloudFrontKeyGroup_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cloudfront_key_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(cloudfront.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, cloudfront.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudFrontKeyGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudFrontKeyGroupConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudFrontKeyGroupExistence(resourceName), + resource.TestCheckResourceAttr("aws_cloudfront_key_group.test", "comment", "test key group"), + resource.TestCheckResourceAttrSet("aws_cloudfront_key_group.test", "etag"), + resource.TestCheckResourceAttrSet("aws_cloudfront_key_group.test", "id"), + resource.TestCheckResourceAttr("aws_cloudfront_key_group.test", "items.#", "1"), + resource.TestCheckResourceAttr("aws_cloudfront_key_group.test", "name", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSCloudFrontKeyGroup_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cloudfront_key_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(cloudfront.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, cloudfront.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudFrontKeyGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudFrontKeyGroupConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudFrontKeyGroupExistence(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsCloudFrontKeyGroup(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSCloudFrontKeyGroup_Comment(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cloudfront_key_group.test" + + firstComment := "first comment" + secondComment := "second comment" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(cloudfront.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, cloudfront.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudFrontKeyGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudFrontKeyGroupConfigComment(rName, firstComment), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudFrontKeyGroupExistence(resourceName), + resource.TestCheckResourceAttr("aws_cloudfront_key_group.test", "comment", firstComment), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSCloudFrontKeyGroupConfigComment(rName, secondComment), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudFrontKeyGroupExistence(resourceName), + resource.TestCheckResourceAttr("aws_cloudfront_key_group.test", "comment", secondComment), + ), + }, + }, + }) +} + +func TestAccAWSCloudFrontKeyGroup_Items(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cloudfront_key_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(cloudfront.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, cloudfront.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudFrontKeyGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudFrontKeyGroupConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudFrontKeyGroupExistence(resourceName), + resource.TestCheckResourceAttr("aws_cloudfront_key_group.test", "items.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSCloudFrontKeyGroupConfigItems(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudFrontKeyGroupExistence(resourceName), + resource.TestCheckResourceAttr("aws_cloudfront_key_group.test", "items.#", "2"), + ), + }, + }, + }) +} + +func testAccCheckCloudFrontKeyGroupExistence(r string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[r] + if !ok { + return fmt.Errorf("not found: %s", r) + } + if rs.Primary.ID == "" { + return fmt.Errorf("no Id is set") + } + + conn := testAccProvider.Meta().(*AWSClient).cloudfrontconn + + input := &cloudfront.GetKeyGroupInput{ + Id: aws.String(rs.Primary.ID), + } + + _, err := conn.GetKeyGroup(input) + if err != nil { + return fmt.Errorf("error retrieving CloudFront key group: %s", err) + } + return nil + } +} + +func testAccCheckCloudFrontKeyGroupDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).cloudfrontconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_cloudfront_key_group" { + continue + } + + input := &cloudfront.GetKeyGroupInput{ + Id: aws.String(rs.Primary.ID), + } + + _, err := conn.GetKeyGroup(input) + if isAWSErr(err, cloudfront.ErrCodeNoSuchResource, "") { + continue + } + if err != nil { + return err + } + return fmt.Errorf("CloudFront key group (%s) was not deleted", rs.Primary.ID) + } + + return nil +} + +func testAccAWSCloudFrontKeyGroupConfigBase(rName string) string { + return fmt.Sprintf(` +resource "aws_cloudfront_public_key" "test" { + comment = "test key" + encoded_key = file("test-fixtures/cloudfront-public-key.pem") + name = %q +} +`, rName) +} + +func testAccAWSCloudFrontKeyGroupConfig(rName string) string { + return testAccAWSCloudFrontKeyGroupConfigBase(rName) + fmt.Sprintf(` +resource "aws_cloudfront_key_group" "test" { + comment = "test key group" + items = [aws_cloudfront_public_key.test.id] + name = %q +} +`, rName) +} + +func testAccAWSCloudFrontKeyGroupConfigComment(rName string, comment string) string { + return testAccAWSCloudFrontKeyGroupConfigBase(rName) + fmt.Sprintf(` +resource "aws_cloudfront_key_group" "test" { + comment = %q + items = [aws_cloudfront_public_key.test.id] + name = %q +} +`, comment, rName) +} + +func testAccAWSCloudFrontKeyGroupConfigItems(rName string) string { + return testAccAWSCloudFrontKeyGroupConfigBase(rName) + fmt.Sprintf(` +resource "aws_cloudfront_public_key" "test2" { + comment = "second test key" + encoded_key = file("test-fixtures/cloudfront-public-key.pem") + name = "%[1]s-second" +} + +resource "aws_cloudfront_key_group" "test" { + comment = "test key group" + items = [aws_cloudfront_public_key.test.id, aws_cloudfront_public_key.test2.id] + name = %[1]q +} +`, rName) +} diff --git a/aws/resource_aws_cloudfront_origin_request_policy_test.go b/aws/resource_aws_cloudfront_origin_request_policy_test.go index 52d22bfcd295..008ffe646527 100644 --- a/aws/resource_aws_cloudfront_origin_request_policy_test.go +++ b/aws/resource_aws_cloudfront_origin_request_policy_test.go @@ -118,7 +118,7 @@ func TestAccAWSCloudFrontOriginRequestPolicy_noneBehavior(t *testing.T) { func testAccAWSCloudFrontOriginRequestPolicyConfig(rInt int) string { return fmt.Sprintf(` resource "aws_cloudfront_origin_request_policy" "example" { - name = "test-policy%[1]d" + name = "test-policyz%[1]d" comment = "test comment" cookies_config { cookie_behavior = "whitelist" diff --git a/website/docs/r/cloudfront_key_group.html.markdown b/website/docs/r/cloudfront_key_group.html.markdown new file mode 100644 index 000000000000..84a8a2169022 --- /dev/null +++ b/website/docs/r/cloudfront_key_group.html.markdown @@ -0,0 +1,42 @@ +--- +subcategory: "CloudFront" +layout: "aws" +page_title: "AWS: aws_cloudfront_key_group" +description: |- + Provides a CloudFront key group. +--- + +# Resource: aws_cloudfront_key_group + +## Example Usage + +The following example below creates a CloudFront key group. + +```terraform +resource "aws_cloudfront_public_key" "example" { + comment = "example public key" + encoded_key = file("public_key.pem") + name = "example-key" +} + +resource "aws_cloudfront_key_group" "example" { + comment = "example key group" + items = [aws_cloudfront_public_key.example.id] + name = "example-key-group" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `comment` - (Optional) A comment to describe the key group.. +* `items` - (Required) A list of the identifiers of the public keys in the key group. +* `name` - (Required) A name to identify the key group. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `etag` - The identifier for this version of the key group. +* `id` - The identifier for the key group.