From b85d0a241bc35b5e4c6387dbd43f3a6d6b9a77a0 Mon Sep 17 00:00:00 2001 From: Sjoerd Tromp Date: Fri, 2 Feb 2024 13:24:05 +0100 Subject: [PATCH 01/10] Adding code to calculate when json needs to be recalculated --- internal/service/ssm/patch_baseline.go | 34 +++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/internal/service/ssm/patch_baseline.go b/internal/service/ssm/patch_baseline.go index a663a5a60f23..58f833e58bfd 100644 --- a/internal/service/ssm/patch_baseline.go +++ b/internal/service/ssm/patch_baseline.go @@ -17,6 +17,7 @@ import ( "github.com/aws/aws-sdk-go/service/ssm" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -230,7 +231,10 @@ func ResourcePatchBaseline() *schema.Resource { names.AttrTagsAll: tftags.TagsSchemaComputed(), }, - CustomizeDiff: verify.SetTagsDiff, + CustomizeDiff: customdiff.Sequence( + resourceObjectCustomizeDiff, + verify.SetTagsDiff, + ), } } @@ -585,3 +589,31 @@ func flattenPatchSource(sources []*ssm.PatchSource) []map[string]interface{} { return result } + +func resourceObjectCustomizeDiff(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + if hasObjectContentChanges(d) { + return d.SetNewComputed("json") + } + + return nil +} + +func hasObjectContentChanges(d verify.ResourceDiffer) bool { + for _, key := range []string{ + "description", + "global_filter", + "approval_rule", + "approved_patches", + "rejected_patches", + "operating_system", + "approved_patches_compliance_level", + "rejected_patches_action", + "approved_patches_enable_non_security", + "source", + } { + if d.HasChange(key) { + return true + } + } + return false +} From d154a3e2e7551b9286c799d3ea8e5038b0f6644d Mon Sep 17 00:00:00 2001 From: Sjoerd Tromp Date: Fri, 2 Feb 2024 13:24:40 +0100 Subject: [PATCH 02/10] Adding test to make sure we detect that the json is marked as 'known after apply' --- internal/service/ssm/patch_baseline_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/internal/service/ssm/patch_baseline_test.go b/internal/service/ssm/patch_baseline_test.go index 170528814f62..9383c7517385 100644 --- a/internal/service/ssm/patch_baseline_test.go +++ b/internal/service/ssm/patch_baseline_test.go @@ -13,7 +13,9 @@ import ( "github.com/aws/aws-sdk-go/service/ssm" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfssm "github.com/hashicorp/terraform-provider-aws/internal/service/ssm" @@ -32,6 +34,11 @@ func TestAccSSMPatchBaseline_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccPatchBaselineConfig_basic(name), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectUnknownValue(resourceName, tfjsonpath.New("json")), + }, + }, Check: resource.ComposeTestCheckFunc( testAccCheckPatchBaselineExists(ctx, resourceName, &before), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "ssm", regexache.MustCompile(`patchbaseline/pb-.+`)), @@ -58,6 +65,11 @@ func TestAccSSMPatchBaseline_basic(t *testing.T) { }, { Config: testAccPatchBaselineConfig_basicUpdated(name), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectUnknownValue(resourceName, tfjsonpath.New("json")), + }, + }, Check: resource.ComposeTestCheckFunc( testAccCheckPatchBaselineExists(ctx, resourceName, &after), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "ssm", regexache.MustCompile(`patchbaseline/pb-.+`)), From b5290dffe79dc31da27bcf3bcfc0f5d0bdb6579f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Feb 2024 11:20:18 -0500 Subject: [PATCH 03/10] Add CHANGELOG entry. --- .changelog/35606.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/35606.txt diff --git a/.changelog/35606.txt b/.changelog/35606.txt new file mode 100644 index 000000000000..9727c6fed534 --- /dev/null +++ b/.changelog/35606.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_ssm_patch_baseline: Mark `json` as Computed if there are content changes +``` \ No newline at end of file From 1292558c75db730be2aeaab913561b11100123f1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Feb 2024 11:23:34 -0500 Subject: [PATCH 04/10] r/aws_ssm_patch_baseline: Alphabetize attributes. --- internal/service/ssm/patch_baseline.go | 145 +++++++++++-------------- 1 file changed, 65 insertions(+), 80 deletions(-) diff --git a/internal/service/ssm/patch_baseline.go b/internal/service/ssm/patch_baseline.go index 58f833e58bfd..d80a086f493d 100644 --- a/internal/service/ssm/patch_baseline.go +++ b/internal/service/ssm/patch_baseline.go @@ -42,50 +42,6 @@ func ResourcePatchBaseline() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(3, 128), - validation.StringMatch(regexache.MustCompile(`^[0-9A-Za-z_.-]{3,128}$`), "must contain only alphanumeric, underscore, hyphen, or period characters"), - ), - }, - - "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringLenBetween(0, 1024), - }, - - "global_filter": { - Type: schema.TypeList, - Optional: true, - MaxItems: 4, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "key": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(ssm.PatchFilterKey_Values(), false), - }, - "values": { - Type: schema.TypeList, - Required: true, - MaxItems: 20, - MinItems: 1, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringLenBetween(1, 64), - }, - }, - }, - }, - }, - "approval_rule": { Type: schema.TypeList, Optional: true, @@ -96,26 +52,22 @@ func ResourcePatchBaseline() *schema.Resource { Optional: true, ValidateFunc: validation.IntBetween(0, 100), }, - "approve_until_date": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringMatch(regexache.MustCompile(`([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))`), "must be formatted YYYY-MM-DD"), }, - "compliance_level": { Type: schema.TypeString, Optional: true, Default: ssm.PatchComplianceLevelUnspecified, ValidateFunc: validation.StringInSlice(ssm.PatchComplianceLevel_Values(), false), }, - "enable_non_security": { Type: schema.TypeBool, Optional: true, Default: false, }, - "patch_filter": { Type: schema.TypeList, Required: true, @@ -143,7 +95,6 @@ func ResourcePatchBaseline() *schema.Resource { }, }, }, - "approved_patches": { Type: schema.TypeSet, Optional: true, @@ -153,17 +104,61 @@ func ResourcePatchBaseline() *schema.Resource { ValidateFunc: validation.StringLenBetween(1, 100), }, }, - - "rejected_patches": { - Type: schema.TypeSet, + "approved_patches_compliance_level": { + Type: schema.TypeString, + Optional: true, + Default: ssm.PatchComplianceLevelUnspecified, + ValidateFunc: validation.StringInSlice(ssm.PatchComplianceLevel_Values(), false), + }, + "approved_patches_enable_non_security": { + Type: schema.TypeBool, Optional: true, - MaxItems: 50, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringLenBetween(1, 100), + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 1024), + }, + "global_filter": { + Type: schema.TypeList, + Optional: true, + MaxItems: 4, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(ssm.PatchFilterKey_Values(), false), + }, + "values": { + Type: schema.TypeList, + Required: true, + MaxItems: 20, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + }, + }, }, }, - + "json": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(3, 128), + validation.StringMatch(regexache.MustCompile(`^[0-9A-Za-z_.-]{3,128}$`), "must contain only alphanumeric, underscore, hyphen, or period characters"), + ), + }, "operating_system": { Type: schema.TypeString, Optional: true, @@ -171,12 +166,14 @@ func ResourcePatchBaseline() *schema.Resource { Default: ssm.OperatingSystemWindows, ValidateFunc: validation.StringInSlice(ssm.OperatingSystem_Values(), false), }, - - "approved_patches_compliance_level": { - Type: schema.TypeString, - Optional: true, - Default: ssm.PatchComplianceLevelUnspecified, - ValidateFunc: validation.StringInSlice(ssm.PatchComplianceLevel_Values(), false), + "rejected_patches": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 50, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringLenBetween(1, 100), + }, }, "rejected_patches_action": { Type: schema.TypeString, @@ -184,17 +181,17 @@ func ResourcePatchBaseline() *schema.Resource { Computed: true, ValidateFunc: validation.StringInSlice(ssm.PatchAction_Values(), false), }, - "approved_patches_enable_non_security": { - Type: schema.TypeBool, - Optional: true, - }, - "source": { Type: schema.TypeList, Optional: true, MaxItems: 20, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "configuration": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, "name": { Type: schema.TypeString, Required: true, @@ -203,13 +200,6 @@ func ResourcePatchBaseline() *schema.Resource { validation.StringMatch(regexache.MustCompile(`^[0-9A-Za-z_.-]{3,50}$`), "must contain only alphanumeric, underscore, hyphen, or period characters"), ), }, - - "configuration": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 1024), - }, - "products": { Type: schema.TypeList, Required: true, @@ -222,11 +212,6 @@ func ResourcePatchBaseline() *schema.Resource { }, }, }, - "json": { - Type: schema.TypeString, - Computed: true, - }, - names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), }, From 84afbd6e7133c5648ebc8fceea5d010f29d82a7c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Feb 2024 11:25:46 -0500 Subject: [PATCH 05/10] r/aws_ssm_patch_baseline: Tidy up Create. --- internal/service/ssm/patch_baseline.go | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/service/ssm/patch_baseline.go b/internal/service/ssm/patch_baseline.go index d80a086f493d..e64a248fc042 100644 --- a/internal/service/ssm/patch_baseline.go +++ b/internal/service/ssm/patch_baseline.go @@ -239,38 +239,38 @@ func resourcePatchBaselineCreate(ctx context.Context, d *schema.ResourceData, me Tags: getTagsIn(ctx), } - if v, ok := d.GetOk("description"); ok { - input.Description = aws.String(v.(string)) + if _, ok := d.GetOk("approval_rule"); ok { + input.ApprovalRules = expandPatchRuleGroup(d) } if v, ok := d.GetOk("approved_patches"); ok && v.(*schema.Set).Len() > 0 { input.ApprovedPatches = flex.ExpandStringSet(v.(*schema.Set)) } - if v, ok := d.GetOk("rejected_patches"); ok && v.(*schema.Set).Len() > 0 { - input.RejectedPatches = flex.ExpandStringSet(v.(*schema.Set)) - } - - if _, ok := d.GetOk("global_filter"); ok { - input.GlobalFilters = expandPatchFilterGroup(d) + if v, ok := d.GetOk("approved_patches_enable_non_security"); ok { + input.ApprovedPatchesEnableNonSecurity = aws.Bool(v.(bool)) } - if _, ok := d.GetOk("approval_rule"); ok { - input.ApprovalRules = expandPatchRuleGroup(d) + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) } - if _, ok := d.GetOk("source"); ok { - input.Sources = expandPatchSource(d) + if _, ok := d.GetOk("global_filter"); ok { + input.GlobalFilters = expandPatchFilterGroup(d) } - if v, ok := d.GetOk("approved_patches_enable_non_security"); ok { - input.ApprovedPatchesEnableNonSecurity = aws.Bool(v.(bool)) + if v, ok := d.GetOk("rejected_patches"); ok && v.(*schema.Set).Len() > 0 { + input.RejectedPatches = flex.ExpandStringSet(v.(*schema.Set)) } if v, ok := d.GetOk("rejected_patches_action"); ok { input.RejectedPatchesAction = aws.String(v.(string)) } + if _, ok := d.GetOk("source"); ok { + input.Sources = expandPatchSource(d) + } + output, err := conn.CreatePatchBaselineWithContext(ctx, input) if err != nil { From d9cd4ed5ffd18cfd3691ab097d88662f02ed2172 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Feb 2024 11:27:02 -0500 Subject: [PATCH 06/10] r/aws_ssm_patch_baseline: Tidy up Delete. --- internal/service/ssm/patch_baseline.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/service/ssm/patch_baseline.go b/internal/service/ssm/patch_baseline.go index e64a248fc042..e5b65fcb28b1 100644 --- a/internal/service/ssm/patch_baseline.go +++ b/internal/service/ssm/patch_baseline.go @@ -404,20 +404,22 @@ func resourcePatchBaselineDelete(ctx context.Context, d *schema.ResourceData, me conn := meta.(*conns.AWSClient).SSMConn(ctx) log.Printf("[INFO] Deleting SSM Patch Baseline: %s", d.Id()) - - params := &ssm.DeletePatchBaselineInput{ + input := &ssm.DeletePatchBaselineInput{ BaselineId: aws.String(d.Id()), } - _, err := conn.DeletePatchBaselineWithContext(ctx, params) + _, err := conn.DeletePatchBaselineWithContext(ctx, input) + if tfawserr.ErrCodeEquals(err, ssm.ErrCodeResourceInUseException) { - // Reset the default patch baseline before retrying + // Reset the default patch baseline before retrying. diags = append(diags, defaultPatchBaselineRestoreOSDefault(ctx, meta.(*conns.AWSClient).SSMClient(ctx), types.OperatingSystem(d.Get("operating_system").(string)))...) if diags.HasError() { return } - _, err = conn.DeletePatchBaselineWithContext(ctx, params) + + _, err = conn.DeletePatchBaselineWithContext(ctx, input) } + if err != nil { diags = sdkdiag.AppendErrorf(diags, "deleting SSM Patch Baseline (%s): %s", d.Id(), err) } From 8e37fb495bf399559302b153016126298a09ac64 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Feb 2024 11:28:16 -0500 Subject: [PATCH 07/10] r/aws_ssm_patch_baseline: Tidy up Update. --- internal/service/ssm/patch_baseline.go | 32 +++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/internal/service/ssm/patch_baseline.go b/internal/service/ssm/patch_baseline.go index e5b65fcb28b1..364540345ef3 100644 --- a/internal/service/ssm/patch_baseline.go +++ b/internal/service/ssm/patch_baseline.go @@ -350,46 +350,46 @@ func resourcePatchBaselineUpdate(ctx context.Context, d *schema.ResourceData, me BaselineId: aws.String(d.Id()), } - if d.HasChange("name") { - input.Name = aws.String(d.Get("name").(string)) - } - - if d.HasChange("description") { - input.Description = aws.String(d.Get("description").(string)) + if d.HasChange("approval_rule") { + input.ApprovalRules = expandPatchRuleGroup(d) } if d.HasChange("approved_patches") { input.ApprovedPatches = flex.ExpandStringSet(d.Get("approved_patches").(*schema.Set)) } - if d.HasChange("rejected_patches") { - input.RejectedPatches = flex.ExpandStringSet(d.Get("rejected_patches").(*schema.Set)) - } - if d.HasChange("approved_patches_compliance_level") { input.ApprovedPatchesComplianceLevel = aws.String(d.Get("approved_patches_compliance_level").(string)) } - if d.HasChange("approval_rule") { - input.ApprovalRules = expandPatchRuleGroup(d) + if d.HasChange("approved_patches_enable_non_security") { + input.ApprovedPatchesEnableNonSecurity = aws.Bool(d.Get("approved_patches_enable_non_security").(bool)) + } + + if d.HasChange("description") { + input.Description = aws.String(d.Get("description").(string)) } if d.HasChange("global_filter") { input.GlobalFilters = expandPatchFilterGroup(d) } - if d.HasChange("source") { - input.Sources = expandPatchSource(d) + if d.HasChange("name") { + input.Name = aws.String(d.Get("name").(string)) } - if d.HasChange("approved_patches_enable_non_security") { - input.ApprovedPatchesEnableNonSecurity = aws.Bool(d.Get("approved_patches_enable_non_security").(bool)) + if d.HasChange("rejected_patches") { + input.RejectedPatches = flex.ExpandStringSet(d.Get("rejected_patches").(*schema.Set)) } if d.HasChange("rejected_patches_action") { input.RejectedPatchesAction = aws.String(d.Get("rejected_patches_action").(string)) } + if d.HasChange("source") { + input.Sources = expandPatchSource(d) + } + _, err := conn.UpdatePatchBaselineWithContext(ctx, input) if err != nil { From 548d55f9f93456b069618aeb6c2bb6370ce7978d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Feb 2024 11:33:51 -0500 Subject: [PATCH 08/10] r/aws_ssm_patch_baseline: Tidy up Read. --- internal/service/ssm/patch_baseline.go | 83 ++++++++++++++++---------- 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/internal/service/ssm/patch_baseline.go b/internal/service/ssm/patch_baseline.go index 364540345ef3..0814ca09cab8 100644 --- a/internal/service/ssm/patch_baseline.go +++ b/internal/service/ssm/patch_baseline.go @@ -18,12 +18,14 @@ import ( "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -286,17 +288,15 @@ func resourcePatchBaselineRead(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).SSMConn(ctx) - params := &ssm.GetPatchBaselineInput{ - BaselineId: aws.String(d.Id()), + output, err := FindPatchBaselineByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] SSM Patch Baseline (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - resp, err := conn.GetPatchBaselineWithContext(ctx, params) if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, ssm.ErrCodeDoesNotExistException) { - log.Printf("[WARN] SSM Patch Baseline (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading SSM Patch Baseline (%s): %s", d.Id(), err) } @@ -306,36 +306,32 @@ func resourcePatchBaselineRead(ctx context.Context, d *schema.ResourceData, meta Service: "ssm", AccountID: meta.(*conns.AWSClient).AccountID, Resource: fmt.Sprintf("patchbaseline/%s", strings.TrimPrefix(d.Id(), "/")), - } + }.String() - jsonDoc, err := json.MarshalIndent(resp, "", " ") + jsonDoc, err := json.MarshalIndent(output, "", " ") if err != nil { - // should never happen if the above code is correct - return sdkdiag.AppendErrorf(diags, "Formatting json representation: formatting JSON: %s", err) + return sdkdiag.AppendFromErr(diags, err) } jsonString := string(jsonDoc) - d.Set("arn", arn.String()) - d.Set("name", resp.Name) - d.Set("description", resp.Description) - d.Set("json", jsonString) - d.Set("operating_system", resp.OperatingSystem) - d.Set("approved_patches_compliance_level", resp.ApprovedPatchesComplianceLevel) - d.Set("approved_patches", flex.FlattenStringList(resp.ApprovedPatches)) - d.Set("rejected_patches", flex.FlattenStringList(resp.RejectedPatches)) - d.Set("rejected_patches_action", resp.RejectedPatchesAction) - d.Set("approved_patches_enable_non_security", resp.ApprovedPatchesEnableNonSecurity) - - if err := d.Set("global_filter", flattenPatchFilterGroup(resp.GlobalFilters)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting global filters: %s", err) + if err := d.Set("approval_rule", flattenPatchRuleGroup(output.ApprovalRules)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting approval_rule: %s", err) } - - if err := d.Set("approval_rule", flattenPatchRuleGroup(resp.ApprovalRules)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting approval rules: %s", err) + d.Set("approved_patches", aws.StringValueSlice(output.ApprovedPatches)) + d.Set("approved_patches_compliance_level", output.ApprovedPatchesComplianceLevel) + d.Set("approved_patches_enable_non_security", output.ApprovedPatchesEnableNonSecurity) + d.Set("arn", arn) + d.Set("description", output.Description) + if err := d.Set("global_filter", flattenPatchFilterGroup(output.GlobalFilters)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting global_filter: %s", err) } - - if err := d.Set("source", flattenPatchSource(resp.Sources)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting patch sources: %s", err) + d.Set("json", jsonString) + d.Set("name", output.Name) + d.Set("operating_system", output.OperatingSystem) + d.Set("rejected_patches", aws.StringValueSlice(output.RejectedPatches)) + d.Set("rejected_patches_action", output.RejectedPatchesAction) + if err := d.Set("source", flattenPatchSource(output.Sources)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting source: %s", err) } return diags @@ -427,6 +423,31 @@ func resourcePatchBaselineDelete(ctx context.Context, d *schema.ResourceData, me return } +func FindPatchBaselineByID(ctx context.Context, conn *ssm.SSM, id string) (*ssm.GetPatchBaselineOutput, error) { + input := &ssm.GetPatchBaselineInput{ + BaselineId: aws.String(id), + } + + output, err := conn.GetPatchBaselineWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, ssm.ErrCodeDoesNotExistException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} + func expandPatchFilterGroup(d *schema.ResourceData) *ssm.PatchFilterGroup { var filters []*ssm.PatchFilter From ce1a686030a2de1a02601038d50250802e168537 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Feb 2024 11:43:20 -0500 Subject: [PATCH 09/10] r/aws_ssm_patch_baseline: Tidy up acceptance tests. --- internal/service/ssm/exports_test.go | 9 +++ internal/service/ssm/patch_baseline.go | 6 +- internal/service/ssm/patch_baseline_test.go | 66 +++++++-------------- internal/service/ssm/service_package_gen.go | 2 +- internal/service/ssm/sweep.go | 2 +- 5 files changed, 35 insertions(+), 50 deletions(-) create mode 100644 internal/service/ssm/exports_test.go diff --git a/internal/service/ssm/exports_test.go b/internal/service/ssm/exports_test.go new file mode 100644 index 000000000000..b5b478d041b3 --- /dev/null +++ b/internal/service/ssm/exports_test.go @@ -0,0 +1,9 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ssm + +// Exports for use in tests only. +var ( + ResourcePatchBaseline = resourcePatchBaseline +) diff --git a/internal/service/ssm/patch_baseline.go b/internal/service/ssm/patch_baseline.go index 0814ca09cab8..0c5a13b858c9 100644 --- a/internal/service/ssm/patch_baseline.go +++ b/internal/service/ssm/patch_baseline.go @@ -32,7 +32,7 @@ import ( // @SDKResource("aws_ssm_patch_baseline", name="Patch Baseline") // @Tags(identifierAttribute="id", resourceType="PatchBaseline") -func ResourcePatchBaseline() *schema.Resource { +func resourcePatchBaseline() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourcePatchBaselineCreate, ReadWithoutTimeout: resourcePatchBaselineRead, @@ -225,10 +225,6 @@ func ResourcePatchBaseline() *schema.Resource { } } -const ( - resNamePatchBaseline = "Patch Baseline" -) - func resourcePatchBaselineCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).SSMConn(ctx) diff --git a/internal/service/ssm/patch_baseline_test.go b/internal/service/ssm/patch_baseline_test.go index 9383c7517385..88b101bedab2 100644 --- a/internal/service/ssm/patch_baseline_test.go +++ b/internal/service/ssm/patch_baseline_test.go @@ -19,13 +19,15 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfssm "github.com/hashicorp/terraform-provider-aws/internal/service/ssm" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccSSMPatchBaseline_basic(t *testing.T) { ctx := acctest.Context(t) - var before, after ssm.PatchBaselineIdentity + var before, after ssm.GetPatchBaselineOutput name := sdkacctest.RandString(10) resourceName := "aws_ssm_patch_baseline.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ssm.EndpointsID), @@ -97,7 +99,7 @@ func TestAccSSMPatchBaseline_basic(t *testing.T) { func TestAccSSMPatchBaseline_tags(t *testing.T) { ctx := acctest.Context(t) - var patch ssm.PatchBaselineIdentity + var patch ssm.GetPatchBaselineOutput name := sdkacctest.RandString(10) resourceName := "aws_ssm_patch_baseline.test" resource.ParallelTest(t, resource.TestCase{ @@ -142,7 +144,7 @@ func TestAccSSMPatchBaseline_tags(t *testing.T) { func TestAccSSMPatchBaseline_disappears(t *testing.T) { ctx := acctest.Context(t) - var identity ssm.PatchBaselineIdentity + var identity ssm.GetPatchBaselineOutput name := sdkacctest.RandString(10) resourceName := "aws_ssm_patch_baseline.test" @@ -166,7 +168,7 @@ func TestAccSSMPatchBaseline_disappears(t *testing.T) { func TestAccSSMPatchBaseline_operatingSystem(t *testing.T) { ctx := acctest.Context(t) - var before, after ssm.PatchBaselineIdentity + var before, after ssm.GetPatchBaselineOutput name := sdkacctest.RandString(10) resourceName := "aws_ssm_patch_baseline.test" resource.ParallelTest(t, resource.TestCase{ @@ -210,7 +212,7 @@ func TestAccSSMPatchBaseline_operatingSystem(t *testing.T) { func TestAccSSMPatchBaseline_approveUntilDateParam(t *testing.T) { ctx := acctest.Context(t) - var before, after ssm.PatchBaselineIdentity + var before, after ssm.GetPatchBaselineOutput name := sdkacctest.RandString(10) resourceName := "aws_ssm_patch_baseline.test" @@ -260,7 +262,7 @@ func TestAccSSMPatchBaseline_approveUntilDateParam(t *testing.T) { func TestAccSSMPatchBaseline_sources(t *testing.T) { ctx := acctest.Context(t) - var before, after ssm.PatchBaselineIdentity + var before, after ssm.GetPatchBaselineOutput name := sdkacctest.RandString(10) resourceName := "aws_ssm_patch_baseline.test" @@ -314,7 +316,7 @@ func TestAccSSMPatchBaseline_sources(t *testing.T) { func TestAccSSMPatchBaseline_approvedPatchesNonSec(t *testing.T) { ctx := acctest.Context(t) - var ssmPatch ssm.PatchBaselineIdentity + var ssmPatch ssm.GetPatchBaselineOutput name := sdkacctest.RandString(10) resourceName := "aws_ssm_patch_baseline.test" @@ -342,7 +344,7 @@ func TestAccSSMPatchBaseline_approvedPatchesNonSec(t *testing.T) { func TestAccSSMPatchBaseline_rejectPatchesAction(t *testing.T) { ctx := acctest.Context(t) - var ssmPatch ssm.PatchBaselineIdentity + var ssmPatch ssm.GetPatchBaselineOutput name := sdkacctest.RandString(10) resourceName := "aws_ssm_patch_baseline.test" @@ -372,7 +374,7 @@ func TestAccSSMPatchBaseline_rejectPatchesAction(t *testing.T) { // Default Patch Baseline acceptance tests because it sets the default patch baseline func testAccSSMPatchBaseline_deleteDefault(t *testing.T) { ctx := acctest.Context(t) - var ssmPatch ssm.PatchBaselineIdentity + var ssmPatch ssm.GetPatchBaselineOutput name := sdkacctest.RandString(10) resourceName := "aws_ssm_patch_baseline.test" @@ -406,7 +408,7 @@ func testAccSSMPatchBaseline_deleteDefault(t *testing.T) { } func testAccCheckPatchBaselineRecreated(t *testing.T, - before, after *ssm.PatchBaselineIdentity) resource.TestCheckFunc { + before, after *ssm.GetPatchBaselineOutput) resource.TestCheckFunc { return func(s *terraform.State) error { if *before.BaselineId == *after.BaselineId { t.Fatalf("Expected change of SSM Patch Baseline IDs, but both were %v", *before.BaselineId) @@ -415,39 +417,24 @@ func testAccCheckPatchBaselineRecreated(t *testing.T, } } -func testAccCheckPatchBaselineExists(ctx context.Context, n string, patch *ssm.PatchBaselineIdentity) resource.TestCheckFunc { +func testAccCheckPatchBaselineExists(ctx context.Context, n string, v *ssm.GetPatchBaselineOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No SSM Patch Baseline ID is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).SSMConn(ctx) - resp, err := conn.DescribePatchBaselinesWithContext(ctx, &ssm.DescribePatchBaselinesInput{ - Filters: []*ssm.PatchOrchestratorFilter{ - { - Key: aws.String("NAME_PREFIX"), - Values: []*string{aws.String(rs.Primary.Attributes["name"])}, - }, - }, - }) + output, err := tfssm.FindPatchBaselineByID(ctx, conn, rs.Primary.ID) - for _, i := range resp.BaselineIdentities { - if *i.BaselineId == rs.Primary.ID { - *patch = *i - return nil - } - } if err != nil { return err } - return fmt.Errorf("No AWS SSM Patch Baseline found") + *v = *output + + return nil } } @@ -460,24 +447,17 @@ func testAccCheckPatchBaselineDestroy(ctx context.Context) resource.TestCheckFun continue } - out, err := conn.DescribePatchBaselinesWithContext(ctx, &ssm.DescribePatchBaselinesInput{ - Filters: []*ssm.PatchOrchestratorFilter{ - { - Key: aws.String("NAME_PREFIX"), - Values: []*string{aws.String(rs.Primary.Attributes["name"])}, - }, - }, - }) + _, err := tfssm.FindPatchBaselineByID(ctx, conn, rs.Primary.ID) - if err != nil { - return err + if tfresource.NotFound(err) { + continue } - if len(out.BaselineIdentities) > 0 { - return fmt.Errorf("Expected AWS SSM Patch Baseline to be gone, but was still found") + if err != nil { + return err } - return nil + return fmt.Errorf("SSM Patch Baseline %s still exists", rs.Primary.ID) } return nil diff --git a/internal/service/ssm/service_package_gen.go b/internal/service/ssm/service_package_gen.go index 014ec193c336..1f562c7adf01 100644 --- a/internal/service/ssm/service_package_gen.go +++ b/internal/service/ssm/service_package_gen.go @@ -106,7 +106,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourcePatchBaseline, + Factory: resourcePatchBaseline, TypeName: "aws_ssm_patch_baseline", Name: "Patch Baseline", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/ssm/sweep.go b/internal/service/ssm/sweep.go index 0a882b6c194b..269b492ee528 100644 --- a/internal/service/ssm/sweep.go +++ b/internal/service/ssm/sweep.go @@ -202,7 +202,7 @@ func sweepPatchBaselines(region string) error { for _, identity := range page.BaselineIdentities { baselineID := aws.ToString(identity.BaselineId) - r := ResourcePatchBaseline() + r := resourcePatchBaseline() d := r.Data(nil) d.SetId(baselineID) d.Set("operating_system", identity.OperatingSystem) From 76083c6446823fb5d16da997e3b88d7387e4a343 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Feb 2024 11:47:28 -0500 Subject: [PATCH 10/10] r/aws_ssm_default_patch_baseline: Tidy up. --- .../service/ssm/default_patch_baseline.go | 30 +++++++++---------- internal/service/ssm/exports_test.go | 5 +++- internal/service/ssm/patch_baseline.go | 4 +-- internal/service/ssm/service_package_gen.go | 3 +- internal/service/ssm/sweep.go | 2 +- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/internal/service/ssm/default_patch_baseline.go b/internal/service/ssm/default_patch_baseline.go index 76d62a0e37b3..3bf48625b451 100644 --- a/internal/service/ssm/default_patch_baseline.go +++ b/internal/service/ssm/default_patch_baseline.go @@ -33,8 +33,8 @@ const ( patchBaselineIDRegexPattern = `pb-[0-9a-f]{17}` ) -// @SDKResource("aws_ssm_default_patch_baseline") -func ResourceDefaultPatchBaseline() *schema.Resource { +// @SDKResource("aws_ssm_default_patch_baseline", name="Default Patch Baseline") +func resourceDefaultPatchBaseline() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceDefaultPatchBaselineCreate, ReadWithoutTimeout: resourceDefaultPatchBaselineRead, @@ -47,7 +47,7 @@ func ResourceDefaultPatchBaseline() *schema.Resource { if isPatchBaselineID(id) || isPatchBaselineARN(id) { conn := meta.(*conns.AWSClient).SSMClient(ctx) - patchbaseline, err := findPatchBaselineByID(ctx, conn, id) + patchbaseline, err := findPatchBaselineByIDV2(ctx, conn, id) if err != nil { return nil, fmt.Errorf("reading SSM Patch Baseline (%s): %w", id, err) } @@ -171,17 +171,16 @@ const ( func resourceDefaultPatchBaselineCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).SSMClient(ctx) baselineID := d.Get("baseline_id").(string) - patchBaseline, err := findPatchBaselineByID(ctx, conn, baselineID) + patchBaseline, err := findPatchBaselineByIDV2(ctx, conn, baselineID) + if err != nil { - return create.AppendDiagErrorMessage(diags, names.SSM, "registering", ResNameDefaultPatchBaseline, baselineID, - create.ProblemStandardMessage(names.SSM, create.ErrActionReading, resNamePatchBaseline, baselineID, err), - ) + return sdkdiag.AppendErrorf(diags, "reading SSM Patch Baseline (%s): %s", baselineID, err) } + if pbOS, cOS := string(patchBaseline.OperatingSystem), d.Get("operating_system"); pbOS != cOS { return create.AppendDiagErrorMessage(diags, names.SSM, "registering", ResNameDefaultPatchBaseline, baselineID, fmt.Sprintf("Patch Baseline Operating System (%s) does not match %s", pbOS, cOS), @@ -304,16 +303,17 @@ func FindDefaultPatchBaseline(ctx context.Context, conn *ssm.Client, os types.Op return out, nil } -func findPatchBaselineByID(ctx context.Context, conn *ssm.Client, id string) (*ssm.GetPatchBaselineOutput, error) { - in := &ssm.GetPatchBaselineInput{ +func findPatchBaselineByIDV2(ctx context.Context, conn *ssm.Client, id string) (*ssm.GetPatchBaselineOutput, error) { + input := &ssm.GetPatchBaselineInput{ BaselineId: aws.String(id), } - out, err := conn.GetPatchBaseline(ctx, in) + + output, err := conn.GetPatchBaseline(ctx, input) if errs.IsA[*types.DoesNotExistException](err) { return nil, &retry.NotFoundError{ LastError: err, - LastRequest: in, + LastRequest: input, } } @@ -321,11 +321,11 @@ func findPatchBaselineByID(ctx context.Context, conn *ssm.Client, id string) (*s return nil, err } - if out == nil { - return nil, tfresource.NewEmptyResultError(in) + if output == nil { + return nil, tfresource.NewEmptyResultError(input) } - return out, nil + return output, nil } func patchBaselinesPaginator(conn *ssm.Client, filters ...types.PatchOrchestratorFilter) *ssm.DescribePatchBaselinesPaginator { diff --git a/internal/service/ssm/exports_test.go b/internal/service/ssm/exports_test.go index b5b478d041b3..0b059e774e91 100644 --- a/internal/service/ssm/exports_test.go +++ b/internal/service/ssm/exports_test.go @@ -5,5 +5,8 @@ package ssm // Exports for use in tests only. var ( - ResourcePatchBaseline = resourcePatchBaseline + ResourceDefaultPatchBaseline = resourceDefaultPatchBaseline + ResourcePatchBaseline = resourcePatchBaseline + + FindPatchBaselineByID = findPatchBaselineByID ) diff --git a/internal/service/ssm/patch_baseline.go b/internal/service/ssm/patch_baseline.go index 0c5a13b858c9..e8f95b341e47 100644 --- a/internal/service/ssm/patch_baseline.go +++ b/internal/service/ssm/patch_baseline.go @@ -284,7 +284,7 @@ func resourcePatchBaselineRead(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).SSMConn(ctx) - output, err := FindPatchBaselineByID(ctx, conn, d.Id()) + output, err := findPatchBaselineByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] SSM Patch Baseline (%s) not found, removing from state", d.Id()) @@ -419,7 +419,7 @@ func resourcePatchBaselineDelete(ctx context.Context, d *schema.ResourceData, me return } -func FindPatchBaselineByID(ctx context.Context, conn *ssm.SSM, id string) (*ssm.GetPatchBaselineOutput, error) { +func findPatchBaselineByID(ctx context.Context, conn *ssm.SSM, id string) (*ssm.GetPatchBaselineOutput, error) { input := &ssm.GetPatchBaselineInput{ BaselineId: aws.String(id), } diff --git a/internal/service/ssm/service_package_gen.go b/internal/service/ssm/service_package_gen.go index 1f562c7adf01..6f3142c3d20d 100644 --- a/internal/service/ssm/service_package_gen.go +++ b/internal/service/ssm/service_package_gen.go @@ -67,8 +67,9 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka TypeName: "aws_ssm_association", }, { - Factory: ResourceDefaultPatchBaseline, + Factory: resourceDefaultPatchBaseline, TypeName: "aws_ssm_default_patch_baseline", + Name: "Default Patch Baseline", }, { Factory: ResourceDocument, diff --git a/internal/service/ssm/sweep.go b/internal/service/ssm/sweep.go index 269b492ee528..616ec308ace1 100644 --- a/internal/service/ssm/sweep.go +++ b/internal/service/ssm/sweep.go @@ -84,7 +84,7 @@ func sweepDefaultPatchBaselines(region string) error { return b.DefaultBaseline }) { baselineID := aws.ToString(identity.BaselineId) - pb, err := findPatchBaselineByID(ctx, conn, baselineID) + pb, err := findPatchBaselineByIDV2(ctx, conn, baselineID) if err != nil { errs = multierror.Append(errs, fmt.Errorf("reading Patch Baseline (%s): %w", baselineID, err)) continue