From 933837cc746938c3985ee3d04c5eb2c9d2bec5d8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 13 Dec 2021 14:59:47 -0500 Subject: [PATCH] r/aws_cloudsearch_domain_service_access_policy: New resource. --- .changelog/17723.txt | 4 + internal/provider/provider.go | 1 + internal/service/cloudsearch/domain.go | 44 +--- .../domain_service_access_policy.go | 217 ++++++++++++++++++ .../domain_service_access_policy_test.go | 1 + ...domain_service_access_policy.html.markdown | 55 +++++ 6 files changed, 279 insertions(+), 43 deletions(-) create mode 100644 internal/service/cloudsearch/domain_service_access_policy.go create mode 100644 internal/service/cloudsearch/domain_service_access_policy_test.go create mode 100644 website/docs/r/cloudsearch_domain_service_access_policy.html.markdown diff --git a/.changelog/17723.txt b/.changelog/17723.txt index bea8120c0cb7..6f0e5f6d2373 100644 --- a/.changelog/17723.txt +++ b/.changelog/17723.txt @@ -1,3 +1,7 @@ ```release-note:new-resource aws_cloudsearch_domain +``` + +```release-note:new-resource +aws_cloudsearch_domain_service_access_policy ``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 92047cfd0293..b11a4ee8676d 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -880,6 +880,7 @@ func Provider() *schema.Provider { "aws_cloudhsm_v2_hsm": cloudhsmv2.ResourceHSM(), "aws_cloudsearch_domain": cloudsearch.ResourceDomain(), + "aws_cloudsearch_domain_service_access_policy": cloudsearch.ResourceDomainServiceAccessPolicy(), "aws_cloudtrail": cloudtrail.ResourceCloudTrail(), diff --git a/internal/service/cloudsearch/domain.go b/internal/service/cloudsearch/domain.go index 3131f9e15cf9..d36819c4fa49 100644 --- a/internal/service/cloudsearch/domain.go +++ b/internal/service/cloudsearch/domain.go @@ -13,11 +13,9 @@ import ( "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" - "github.com/hashicorp/terraform-provider-aws/internal/verify" ) func ResourceDomain() *schema.Resource { @@ -37,18 +35,6 @@ func ResourceDomain() *schema.Resource { }, Schema: map[string]*schema.Schema{ - // TODO: Separate access policy resource? - // TODO: Is it Required? - "access_policies": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringIsJSON, - DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, - StateFunc: func(v interface{}) string { - json, _ := structure.NormalizeJsonString(v) - return json - }, - }, "arn": { Type: schema.TypeString, Computed: true, @@ -204,17 +190,6 @@ func resourceCloudSearchDomainCreate(d *schema.ResourceData, meta interface{}) e d.SetId(name) - // TODO: Separate domain access policy resource? - // log.Printf("[DEBUG] Updating CloudSearch Domain (%s) access policies", name) - // _, err = conn.UpdateServiceAccessPolicies(&cloudsearch.UpdateServiceAccessPoliciesInput{ - // DomainName: aws.String(d.Id()), - // AccessPolicies: aws.String(d.Get("access_policies").(string)), - // }) - - // if err != nil { - // return fmt.Errorf("error updating CloudSearch Domain (%s) service access policies: %w", d.Id(), err) - // } - if v, ok := d.GetOk("scaling_parameters"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { input := &cloudsearch.UpdateScalingParametersInput{ DomainName: aws.String(d.Id()), @@ -373,30 +348,13 @@ func resourceCloudSearchDomainRead(d *schema.ResourceData, meta interface{}) err } d.Set("index", result) - // Read service access policies. - // policyResult, err := conn.DescribeServiceAccessPolicies(&cloudsearch.DescribeServiceAccessPoliciesInput{ - // DomainName: aws.String(d.Get("name").(string)), - // }) - // if err != nil { - // return err - // } - // d.Set("service_access_policies", policyResult.AccessPolicies.Options) - return err } func resourceCloudSearchDomainUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).CloudSearchConn - _, err := conn.UpdateServiceAccessPolicies(&cloudsearch.UpdateServiceAccessPoliciesInput{ - DomainName: aws.String(d.Get("name").(string)), - AccessPolicies: aws.String(d.Get("service_access_policies").(string)), - }) - if err != nil { - return err - } - - _, err = conn.UpdateScalingParameters(&cloudsearch.UpdateScalingParametersInput{ + _, err := conn.UpdateScalingParameters(&cloudsearch.UpdateScalingParametersInput{ DomainName: aws.String(d.Get("name").(string)), ScalingParameters: &cloudsearch.ScalingParameters{ DesiredInstanceType: aws.String(d.Get("instance_type").(string)), diff --git a/internal/service/cloudsearch/domain_service_access_policy.go b/internal/service/cloudsearch/domain_service_access_policy.go new file mode 100644 index 000000000000..19ba4e868de1 --- /dev/null +++ b/internal/service/cloudsearch/domain_service_access_policy.go @@ -0,0 +1,217 @@ +package cloudsearch + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudsearch" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func ResourceDomainServiceAccessPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceDomainServiceAccessPolicyPut, + Read: resourceDomainServiceAccessPolicyRead, + Update: resourceDomainServiceAccessPolicyPut, + Delete: resourceDomainServiceAccessPolicyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "access_policy": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, + ValidateFunc: validation.StringIsJSON, + StateFunc: func(v interface{}) string { + json, _ := structure.NormalizeJsonString(v) + return json + }, + }, + "domain_name": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceDomainServiceAccessPolicyPut(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).CloudSearchConn + + domainName := d.Get("domain_name").(string) + input := &cloudsearch.UpdateServiceAccessPoliciesInput{ + DomainName: aws.String(domainName), + } + + accessPolicy := d.Get("access_policy").(string) + policy, err := structure.NormalizeJsonString(accessPolicy) + + if err != nil { + return fmt.Errorf("policy (%s) is invalid JSON: %w", accessPolicy, err) + } + + input.AccessPolicies = aws.String(policy) + + log.Printf("[DEBUG] Updating CloudSearch Domain access policies: %s", input) + _, err = conn.UpdateServiceAccessPolicies(input) + + if err != nil { + return fmt.Errorf("error creating CloudSearch Domain Service Access Policy (%s): %w", domainName, err) + } + + d.SetId(domainName) + + _, err = waitAccessPolicyActive(conn, d.Id()) + + if err != nil { + return fmt.Errorf("error waiting for CloudSearch Domain Service Access Policy (%s) to become active: %w", d.Id(), err) + } + + return nil +} + +func resourceDomainServiceAccessPolicyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).CloudSearchConn + + accessPolicy, err := FindAccessPolicyByName(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] CloudSearch Domain Service Access Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading CloudSearch Domain Service Access Policy (%s): %w", d.Id(), err) + } + + policyToSet, err := verify.PolicyToSet(d.Get("access_policy").(string), accessPolicy) + + if err != nil { + return err + } + + d.Set("access_policy", policyToSet) + d.Set("domain_name", d.Id()) + + return nil +} + +func resourceDomainServiceAccessPolicyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).CloudSearchConn + + domainName := d.Get("domain_name").(string) + input := &cloudsearch.UpdateServiceAccessPoliciesInput{ + AccessPolicies: aws.String(""), + DomainName: aws.String(d.Id()), + } + + log.Printf("[DEBUG] Deleting CloudSearch Domain Service Access Policy: %s", d.Id()) + _, err := conn.UpdateServiceAccessPolicies(input) + + if tfawserr.ErrCodeEquals(err, cloudsearch.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting CloudSearch Domain Service Access Policy (%s): %w", d.Id(), err) + } + + _, err = waitAccessPolicyActive(conn, d.Id()) + + if err != nil { + return fmt.Errorf("error waiting for CloudSearch Domain Service Access Policy (%s) to delete: %w", d.Id(), err) + } + + return nil +} + +func FindAccessPolicyByName(conn *cloudsearch.CloudSearch, name string) (string, error) { + output, err := findAccessPoliciesStatusByName(conn, name) + + if err != nil { + return "", err + } + + accessPolicy := aws.StringValue(output.Options) + + if accessPolicy == "" { + return "", tfresource.NewEmptyResultError(name) + } + + return accessPolicy, nil +} + +func findAccessPoliciesStatusByName(conn *cloudsearch.CloudSearch, name string) (*cloudsearch.AccessPoliciesStatus, error) { + input := &cloudsearch.DescribeServiceAccessPoliciesInput{ + Deployed: aws.Bool(true), + DomainName: aws.String(name), + } + + output, err := conn.DescribeServiceAccessPolicies(input) + + if tfawserr.ErrCodeEquals(err, cloudsearch.ErrCodeResourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.AccessPolicies == nil || output.AccessPolicies.Status == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.AccessPolicies, nil +} + +func statusAccessPolicyState(conn *cloudsearch.CloudSearch, name string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findAccessPoliciesStatusByName(conn, name) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Status.State), nil + } +} + +const ( + accessPolicyTimeout = 2 * time.Minute +) + +func waitAccessPolicyActive(conn *cloudsearch.CloudSearch, name string) (*cloudsearch.AccessPoliciesStatus, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{cloudsearch.OptionStateProcessing}, + Target: []string{cloudsearch.OptionStateActive}, + Refresh: statusAccessPolicyState(conn, name), + Timeout: accessPolicyTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*cloudsearch.AccessPoliciesStatus); ok { + return output, err + } + + return nil, err +} diff --git a/internal/service/cloudsearch/domain_service_access_policy_test.go b/internal/service/cloudsearch/domain_service_access_policy_test.go new file mode 100644 index 000000000000..35eb54b18733 --- /dev/null +++ b/internal/service/cloudsearch/domain_service_access_policy_test.go @@ -0,0 +1 @@ +package cloudsearch_test diff --git a/website/docs/r/cloudsearch_domain_service_access_policy.html.markdown b/website/docs/r/cloudsearch_domain_service_access_policy.html.markdown new file mode 100644 index 000000000000..6568648f4fa6 --- /dev/null +++ b/website/docs/r/cloudsearch_domain_service_access_policy.html.markdown @@ -0,0 +1,55 @@ +--- +subcategory: "CloudSearch" +layout: "aws" +page_title: "AWS: aws_cloudsearch_domain_service_access_policy" +description: |- + Provides an CloudSearch domain service access policy resource. +--- + +# Resource: aws_cloudsearch_domain_service_access_policy + +Provides an CloudSearch domain service access policy resource. + +## Example Usage + +```terraform +resource "aws_cloudsearch_domain" "example" { + name = "example-domain" +} + +resource "aws_cloudsearch_domain_service_access_policy" "example" { + domain_name = aws_cloudsearch_domain.example.id + + policy = <