Skip to content

Commit

Permalink
resource/aws_ssm_patch_baseline: Approved/Rejected patch enhancements…
Browse files Browse the repository at this point in the history
…, arn attribute, and validations (#11772)

Output from acceptance testing in AWS Commercial:

```
--- PASS: TestAccAWSSSMPatchBaseline_disappears (11.66s)
--- PASS: TestAccAWSSSMPatchBaseline_ApprovedPatchesNonSec (15.49s)
--- PASS: TestAccAWSSSMPatchBaseline_RejectPatchesAction (15.51s)
--- PASS: TestAccAWSSSMPatchBaseline_basic (24.91s)
--- PASS: TestAccAWSSSMPatchBaseline_OperatingSystem (25.68s)
--- PASS: TestAccAWSSSMPatchBaseline_tags (34.07s)
```

Output from acceptance testing in AWS GovCloud (US):

```
--- PASS: TestAccAWSSSMPatchBaseline_disappears (14.52s)
--- PASS: TestAccAWSSSMPatchBaseline_RejectPatchesAction (19.65s)
--- PASS: TestAccAWSSSMPatchBaseline_ApprovedPatchesNonSec (20.14s)
--- PASS: TestAccAWSSSMPatchBaseline_basic (34.06s)
--- PASS: TestAccAWSSSMPatchBaseline_OperatingSystem (34.41s)
--- PASS: TestAccAWSSSMPatchBaseline_tags (46.50s)
```
  • Loading branch information
DrFaust92 authored Feb 12, 2021
1 parent 596aa3d commit fc1c883
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 64 deletions.
12 changes: 12 additions & 0 deletions .changelog/11772.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
```release-note:enhancement
resource/aws_ssm_patch_baseline: Adds plan time validation for `name`, `description`, `global_filter.key`, `global_filter.values`,
`approved_patches`, `rejected_patches`, `approval_rule.approve_after_days`, `approval_rule.patch_filter.key`, and `approval_rule.patch_filter.values`.
```

```release-note:enhancement
resource/aws_ssm_patch_baseline: Add `approved_patches_enable_non_security` and `rejected_patches_action` arguments
```

```release-note:enhancement
resource/aws_ssm_patch_baseline: Adds `arn` attribute.
```
133 changes: 99 additions & 34 deletions aws/resource_aws_ssm_patch_baseline.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package aws
import (
"fmt"
"log"
"regexp"
"strings"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
Expand All @@ -22,14 +25,23 @@ func resourceAwsSsmPatchBaseline() *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(regexp.MustCompile(`^[a-zA-Z0-9_\-.]{3,128}$`), "must contain only alphanumeric, underscore, hyphen, or period characters"),
),
},

"description": {
Type: schema.TypeString,
Optional: true,
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(0, 1024),
},

"global_filter": {
Expand All @@ -39,13 +51,19 @@ func resourceAwsSsmPatchBaseline() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(ssm.PatchFilterKey_Values(), false),
},
"values": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
MaxItems: 20,
MinItems: 1,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 64),
},
},
},
},
Expand All @@ -57,8 +75,9 @@ func resourceAwsSsmPatchBaseline() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"approve_after_days": {
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(0, 100),
},

"compliance_level": {
Expand All @@ -81,13 +100,19 @@ func resourceAwsSsmPatchBaseline() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(ssm.PatchFilterKey_Values(), false),
},
"values": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
MaxItems: 20,
MinItems: 1,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 64),
},
},
},
},
Expand All @@ -99,15 +124,21 @@ func resourceAwsSsmPatchBaseline() *schema.Resource {
"approved_patches": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
MaxItems: 50,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 100),
},
},

"rejected_patches": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
MaxItems: 50,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 100),
},
},

"operating_system": {
Expand All @@ -124,13 +155,23 @@ func resourceAwsSsmPatchBaseline() *schema.Resource {
Default: ssm.PatchComplianceLevelUnspecified,
ValidateFunc: validation.StringInSlice(ssm.PatchComplianceLevel_Values(), false),
},
"rejected_patches_action": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice(ssm.PatchAction_Values(), false),
},
"approved_patches_enable_non_security": {
Type: schema.TypeBool,
Optional: true,
},
"tags": tagsSchema(),
},
}
}

func resourceAwsSsmPatchBaselineCreate(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn
conn := meta.(*AWSClient).ssmconn

params := &ssm.CreatePatchBaselineInput{
Name: aws.String(d.Get("name").(string)),
Expand Down Expand Up @@ -162,7 +203,15 @@ func resourceAwsSsmPatchBaselineCreate(d *schema.ResourceData, meta interface{})
params.ApprovalRules = expandAwsSsmPatchRuleGroup(d)
}

resp, err := ssmconn.CreatePatchBaseline(params)
if v, ok := d.GetOk("approved_patches_enable_non_security"); ok {
params.ApprovedPatchesEnableNonSecurity = aws.Bool(v.(bool))
}

if v, ok := d.GetOk("rejected_patches_action"); ok {
params.RejectedPatchesAction = aws.String(v.(string))
}

resp, err := conn.CreatePatchBaseline(params)
if err != nil {
return err
}
Expand All @@ -172,7 +221,7 @@ func resourceAwsSsmPatchBaselineCreate(d *schema.ResourceData, meta interface{})
}

func resourceAwsSsmPatchBaselineUpdate(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn
conn := meta.(*AWSClient).ssmconn

params := &ssm.UpdatePatchBaselineInput{
BaselineId: aws.String(d.Id()),
Expand Down Expand Up @@ -206,35 +255,40 @@ func resourceAwsSsmPatchBaselineUpdate(d *schema.ResourceData, meta interface{})
params.GlobalFilters = expandAwsSsmPatchFilterGroup(d)
}

_, err := ssmconn.UpdatePatchBaseline(params)
if err != nil {
if isAWSErr(err, ssm.ErrCodeDoesNotExistException, "") {
log.Printf("[WARN] Patch Baseline %s not found, removing from state", d.Id())
d.SetId("")
return nil
if d.HasChange("approved_patches_enable_non_security") {
params.ApprovedPatchesEnableNonSecurity = aws.Bool(d.Get("approved_patches_enable_non_security").(bool))
}

if d.HasChange("rejected_patches_action") {
params.RejectedPatchesAction = aws.String(d.Get("rejected_patches_action").(string))
}

if d.HasChangesExcept("tags") {
_, err := conn.UpdatePatchBaseline(params)
if err != nil {
return fmt.Errorf("error updating SSM Patch Baseline (%s): %w", d.Id(), err)
}
return err
}

if d.HasChange("tags") {
o, n := d.GetChange("tags")

if err := keyvaluetags.SsmUpdateTags(ssmconn, d.Id(), ssm.ResourceTypeForTaggingPatchBaseline, o, n); err != nil {
if err := keyvaluetags.SsmUpdateTags(conn, d.Id(), ssm.ResourceTypeForTaggingPatchBaseline, o, n); err != nil {
return fmt.Errorf("error updating SSM Patch Baseline (%s) tags: %s", d.Id(), err)
}
}

return resourceAwsSsmPatchBaselineRead(d, meta)
}
func resourceAwsSsmPatchBaselineRead(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn
conn := meta.(*AWSClient).ssmconn
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

params := &ssm.GetPatchBaselineInput{
BaselineId: aws.String(d.Id()),
}

resp, err := ssmconn.GetPatchBaseline(params)
resp, err := conn.GetPatchBaseline(params)
if err != nil {
if isAWSErr(err, ssm.ErrCodeDoesNotExistException, "") {
log.Printf("[WARN] Patch Baseline %s not found, removing from state", d.Id())
Expand All @@ -250,6 +304,8 @@ func resourceAwsSsmPatchBaselineRead(d *schema.ResourceData, meta interface{}) e
d.Set("approved_patches_compliance_level", resp.ApprovedPatchesComplianceLevel)
d.Set("approved_patches", flattenStringList(resp.ApprovedPatches))
d.Set("rejected_patches", 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", flattenAwsSsmPatchFilterGroup(resp.GlobalFilters)); err != nil {
return fmt.Errorf("Error setting global filters error: %#v", err)
Expand All @@ -259,7 +315,16 @@ func resourceAwsSsmPatchBaselineRead(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("Error setting approval rules error: %#v", err)
}

tags, err := keyvaluetags.SsmListTags(ssmconn, d.Id(), ssm.ResourceTypeForTaggingPatchBaseline)
arn := arn.ARN{
Partition: meta.(*AWSClient).partition,
Region: meta.(*AWSClient).region,
Service: "ssm",
AccountID: meta.(*AWSClient).accountid,
Resource: fmt.Sprintf("patchbaseline/%s", strings.TrimPrefix(d.Id(), "/")),
}
d.Set("arn", arn.String())

tags, err := keyvaluetags.SsmListTags(conn, d.Id(), ssm.ResourceTypeForTaggingPatchBaseline)

if err != nil {
return fmt.Errorf("error listing tags for SSM Patch Baseline (%s): %s", d.Id(), err)
Expand All @@ -273,15 +338,15 @@ func resourceAwsSsmPatchBaselineRead(d *schema.ResourceData, meta interface{}) e
}

func resourceAwsSsmPatchBaselineDelete(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn
conn := meta.(*AWSClient).ssmconn

log.Printf("[INFO] Deleting SSM Patch Baseline: %s", d.Id())

params := &ssm.DeletePatchBaselineInput{
BaselineId: aws.String(d.Id()),
}

_, err := ssmconn.DeletePatchBaseline(params)
_, err := conn.DeletePatchBaseline(params)
if err != nil {
return fmt.Errorf("error deleting SSM Patch Baseline (%s): %s", d.Id(), err)
}
Expand Down Expand Up @@ -319,7 +384,7 @@ func flattenAwsSsmPatchFilterGroup(group *ssm.PatchFilterGroup) []map[string]int

for _, filter := range group.PatchFilters {
f := make(map[string]interface{})
f["key"] = *filter.Key
f["key"] = aws.StringValue(filter.Key)
f["values"] = flattenStringList(filter.Values)

result = append(result, f)
Expand Down Expand Up @@ -378,9 +443,9 @@ func flattenAwsSsmPatchRuleGroup(group *ssm.PatchRuleGroup) []map[string]interfa

for _, rule := range group.PatchRules {
r := make(map[string]interface{})
r["approve_after_days"] = *rule.ApproveAfterDays
r["compliance_level"] = *rule.ComplianceLevel
r["enable_non_security"] = *rule.EnableNonSecurity
r["approve_after_days"] = aws.Int64Value(rule.ApproveAfterDays)
r["compliance_level"] = aws.StringValue(rule.ComplianceLevel)
r["enable_non_security"] = aws.BoolValue(rule.EnableNonSecurity)
r["patch_filter"] = flattenAwsSsmPatchFilterGroup(rule.PatchFilterGroup)
result = append(result, r)
}
Expand Down
Loading

0 comments on commit fc1c883

Please sign in to comment.