From ad459eca2cf18edf433fce982691c8c08771dad7 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 15 Jul 2020 13:15:46 -0400 Subject: [PATCH] resource/aws_iam_server_certificate: Remove state hashing from certificate_body, certificate_chain, and private_key arguments Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/13406 Output from acceptance testing: ``` --- PASS: TestAccAWSIAMServerCertificate_name_prefix (6.05s) --- PASS: TestAccAWSIAMServerCertificate_disappears (6.10s) --- PASS: TestAccAWSIAMServerCertificate_Path (6.48s) --- PASS: TestAccAWSIAMServerCertificate_basic (6.54s) --- PASS: TestAccAWSIAMServerCertificate_file (10.07s) ``` --- aws/resource_aws_iam_server_certificate.go | 71 +++++++++---------- aws/state_funcs.go | 17 +++++ website/docs/guides/version-3-upgrade.html.md | 7 ++ 3 files changed, 58 insertions(+), 37 deletions(-) create mode 100644 aws/state_funcs.go diff --git a/aws/resource_aws_iam_server_certificate.go b/aws/resource_aws_iam_server_certificate.go index 658bc181f9b..9dd745240d5 100644 --- a/aws/resource_aws_iam_server_certificate.go +++ b/aws/resource_aws_iam_server_certificate.go @@ -29,17 +29,19 @@ func resourceAwsIAMServerCertificate() *schema.Resource { Schema: map[string]*schema.Schema{ "certificate_body": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - StateFunc: normalizeCert, + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: suppressNormalizeCertRemoval, + StateFunc: StateTrimSpace, }, "certificate_chain": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - StateFunc: normalizeCert, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: suppressNormalizeCertRemoval, + StateFunc: StateTrimSpace, }, "path": { @@ -50,11 +52,12 @@ func resourceAwsIAMServerCertificate() *schema.Resource { }, "private_key": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - StateFunc: normalizeCert, - Sensitive: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + DiffSuppressFunc: suppressNormalizeCertRemoval, + StateFunc: StateTrimSpace, }, "name": { @@ -112,13 +115,10 @@ func resourceAwsIAMServerCertificateCreate(d *schema.ResourceData, meta interfac log.Printf("[DEBUG] Creating IAM Server Certificate with opts: %s", createOpts) resp, err := conn.UploadServerCertificate(createOpts) if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - return fmt.Errorf("Error uploading server certificate, error: %s: %s", awsErr.Code(), awsErr.Message()) - } - return fmt.Errorf("Error uploading server certificate, error: %s", err) + return fmt.Errorf("error uploading server certificate: %w", err) } - d.SetId(*resp.ServerCertificateMetadata.ServerCertificateId) + d.SetId(aws.StringValue(resp.ServerCertificateMetadata.ServerCertificateId)) d.Set("name", sslCertName) return resourceAwsIAMServerCertificateRead(d, meta) @@ -130,29 +130,20 @@ func resourceAwsIAMServerCertificateRead(d *schema.ResourceData, meta interface{ ServerCertificateName: aws.String(d.Get("name").(string)), }) - if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "NoSuchEntity" { - log.Printf("[WARN] IAM Server Cert (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return fmt.Errorf("Error reading IAM Server Certificate: %s: %s", awsErr.Code(), awsErr.Message()) - } - return fmt.Errorf("Error reading IAM Server Certificate: %s", err) + if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") { + log.Printf("[WARN] IAM Server Certificate (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } - d.SetId(*resp.ServerCertificate.ServerCertificateMetadata.ServerCertificateId) - - // these values should always be present, and have a default if not set in - // configuration, and so safe to reference with nil checks - d.Set("certificate_body", normalizeCert(resp.ServerCertificate.CertificateBody)) - - c := normalizeCert(resp.ServerCertificate.CertificateChain) - if c != "" { - d.Set("certificate_chain", c) + if err != nil { + return fmt.Errorf("error reading IAM Server Certificate (%s): %w", d.Id(), err) } + d.SetId(aws.StringValue(resp.ServerCertificate.ServerCertificateMetadata.ServerCertificateId)) + + d.Set("certificate_body", resp.ServerCertificate.CertificateBody) + d.Set("certificate_chain", resp.ServerCertificate.CertificateChain) d.Set("path", resp.ServerCertificate.ServerCertificateMetadata.Path) d.Set("arn", resp.ServerCertificate.ServerCertificateMetadata.Arn) @@ -247,3 +238,9 @@ func stripCR(b []byte) []byte { } return c[:i] } + +// Terraform AWS Provider version 3.0.0 removed state hash storage. +// This DiffSuppressFunc prevents the resource from triggering needless recreation. +func suppressNormalizeCertRemoval(k, old, new string, d *schema.ResourceData) bool { + return normalizeCert(new) == old +} diff --git a/aws/state_funcs.go b/aws/state_funcs.go new file mode 100644 index 00000000000..9485b2c6878 --- /dev/null +++ b/aws/state_funcs.go @@ -0,0 +1,17 @@ +package aws + +import "strings" + +// StateTrimSpace is a StateFunc that trims extraneous whitespace from strings. +// +// This prevents differences caused by an API canonicalizing a string with a +// trailing newline character removed. +func StateTrimSpace(v interface{}) string { + s, ok := v.(string) + + if !ok { + return "" + } + + return strings.TrimSpace(s) +} diff --git a/website/docs/guides/version-3-upgrade.html.md b/website/docs/guides/version-3-upgrade.html.md index 99c3bb87349..8f769cf2819 100644 --- a/website/docs/guides/version-3-upgrade.html.md +++ b/website/docs/guides/version-3-upgrade.html.md @@ -26,6 +26,7 @@ Upgrade topics: - [Resource: aws_dx_gateway](#resource-aws_dx_gateway) - [Resource: aws_elastic_transcoder_preset](#resource-aws_elastic_transcoder_preset) - [Resource: aws_emr_cluster](#resource-aws_emr_cluster) +- [Resource: aws_iam_server_certificate](#resource-aws_iam_server_certificate) - [Resource: aws_lb_listener_rule](#resource-aws_lb_listener_rule) - [Resource: aws_msk_cluster](#resource-aws_msk_cluster) - [Resource: aws_s3_bucket](#resource-aws_s3_bucket) @@ -370,6 +371,12 @@ resource "aws_emr_cluster" "example" { } ``` +## Resource: aws_iam_server_certificate + +### certificate_body, certificate_chain, and private_key Arguments No Longer Stored as Hash + +Previously when the `certificate_body`, `certificate_chain`, and `private_key` arguments were stored in state, they were stored as a hash of the actual value. This hashing has been removed for new or recreated resources to prevent lifecycle issues. + ## Resource: aws_lb_listener_rule ### condition.field and condition.values Arguments Removal