provider/aws: Add support for aws_ssm_patch_baseline (#14954)
* Add support for aws_ssm_patch_baseline and aws_ssm_patch_group * Fix failing test * Cleanup commented out code
This commit is contained in:
parent
d400fe23e0
commit
d3eed78d95
|
@ -430,6 +430,8 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_ssm_maintenance_window": resourceAwsSsmMaintenanceWindow(),
|
"aws_ssm_maintenance_window": resourceAwsSsmMaintenanceWindow(),
|
||||||
"aws_ssm_maintenance_window_target": resourceAwsSsmMaintenanceWindowTarget(),
|
"aws_ssm_maintenance_window_target": resourceAwsSsmMaintenanceWindowTarget(),
|
||||||
"aws_ssm_maintenance_window_task": resourceAwsSsmMaintenanceWindowTask(),
|
"aws_ssm_maintenance_window_task": resourceAwsSsmMaintenanceWindowTask(),
|
||||||
|
"aws_ssm_patch_baseline": resourceAwsSsmPatchBaseline(),
|
||||||
|
"aws_ssm_patch_group": resourceAwsSsmPatchGroup(),
|
||||||
"aws_spot_datafeed_subscription": resourceAwsSpotDataFeedSubscription(),
|
"aws_spot_datafeed_subscription": resourceAwsSpotDataFeedSubscription(),
|
||||||
"aws_spot_instance_request": resourceAwsSpotInstanceRequest(),
|
"aws_spot_instance_request": resourceAwsSpotInstanceRequest(),
|
||||||
"aws_spot_fleet_request": resourceAwsSpotFleetRequest(),
|
"aws_spot_fleet_request": resourceAwsSpotFleetRequest(),
|
||||||
|
|
|
@ -0,0 +1,277 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsSsmPatchBaseline() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsSsmPatchBaselineCreate,
|
||||||
|
Read: resourceAwsSsmPatchBaselineRead,
|
||||||
|
Delete: resourceAwsSsmPatchBaselineDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"description": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"global_filter": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
MaxItems: 4,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"key": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"values": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Required: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"approval_rule": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"approve_after_days": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"patch_filter": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Required: true,
|
||||||
|
MaxItems: 10,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"key": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"values": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Required: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"approved_patches": {
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
|
||||||
|
"rejected_patches": {
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsSsmPatchBaselineCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
ssmconn := meta.(*AWSClient).ssmconn
|
||||||
|
|
||||||
|
params := &ssm.CreatePatchBaselineInput{
|
||||||
|
Name: aws.String(d.Get("name").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("description"); ok {
|
||||||
|
params.Description = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("approved_patches"); ok && v.(*schema.Set).Len() > 0 {
|
||||||
|
params.ApprovedPatches = expandStringList(v.(*schema.Set).List())
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("rejected_patches"); ok && v.(*schema.Set).Len() > 0 {
|
||||||
|
params.RejectedPatches = expandStringList(v.(*schema.Set).List())
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := d.GetOk("global_filter"); ok {
|
||||||
|
params.GlobalFilters = expandAwsSsmPatchFilterGroup(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := d.GetOk("approval_rule"); ok {
|
||||||
|
params.ApprovalRules = expandAwsSsmPatchRuleGroup(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ssmconn.CreatePatchBaseline(params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(*resp.BaselineId)
|
||||||
|
return resourceAwsSsmPatchBaselineRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsSsmPatchBaselineRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
ssmconn := meta.(*AWSClient).ssmconn
|
||||||
|
|
||||||
|
params := &ssm.GetPatchBaselineInput{
|
||||||
|
BaselineId: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ssmconn.GetPatchBaseline(params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("name", resp.Name)
|
||||||
|
d.Set("description", resp.Description)
|
||||||
|
d.Set("approved_patches", flattenStringList(resp.ApprovedPatches))
|
||||||
|
d.Set("rejected_patches", flattenStringList(resp.RejectedPatches))
|
||||||
|
|
||||||
|
if err := d.Set("global_filter", flattenAwsSsmPatchFilterGroup(resp.GlobalFilters)); err != nil {
|
||||||
|
return fmt.Errorf("[DEBUG] Error setting global filters error: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Set("approval_rule", flattenAwsSsmPatchRuleGroup(resp.ApprovalRules)); err != nil {
|
||||||
|
return fmt.Errorf("[DEBUG] Error setting approval rules error: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsSsmPatchBaselineDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
ssmconn := 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)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandAwsSsmPatchFilterGroup(d *schema.ResourceData) *ssm.PatchFilterGroup {
|
||||||
|
var filters []*ssm.PatchFilter
|
||||||
|
|
||||||
|
filterConfig := d.Get("global_filter").([]interface{})
|
||||||
|
|
||||||
|
for _, fConfig := range filterConfig {
|
||||||
|
config := fConfig.(map[string]interface{})
|
||||||
|
|
||||||
|
filter := &ssm.PatchFilter{
|
||||||
|
Key: aws.String(config["key"].(string)),
|
||||||
|
Values: expandStringList(config["values"].([]interface{})),
|
||||||
|
}
|
||||||
|
|
||||||
|
filters = append(filters, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ssm.PatchFilterGroup{
|
||||||
|
PatchFilters: filters,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenAwsSsmPatchFilterGroup(group *ssm.PatchFilterGroup) []map[string]interface{} {
|
||||||
|
if len(group.PatchFilters) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]map[string]interface{}, 0, len(group.PatchFilters))
|
||||||
|
|
||||||
|
for _, filter := range group.PatchFilters {
|
||||||
|
f := make(map[string]interface{})
|
||||||
|
f["key"] = *filter.Key
|
||||||
|
f["values"] = flattenStringList(filter.Values)
|
||||||
|
|
||||||
|
result = append(result, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandAwsSsmPatchRuleGroup(d *schema.ResourceData) *ssm.PatchRuleGroup {
|
||||||
|
var rules []*ssm.PatchRule
|
||||||
|
|
||||||
|
ruleConfig := d.Get("approval_rule").([]interface{})
|
||||||
|
|
||||||
|
for _, rConfig := range ruleConfig {
|
||||||
|
rCfg := rConfig.(map[string]interface{})
|
||||||
|
|
||||||
|
var filters []*ssm.PatchFilter
|
||||||
|
filterConfig := rCfg["patch_filter"].([]interface{})
|
||||||
|
|
||||||
|
for _, fConfig := range filterConfig {
|
||||||
|
fCfg := fConfig.(map[string]interface{})
|
||||||
|
|
||||||
|
filter := &ssm.PatchFilter{
|
||||||
|
Key: aws.String(fCfg["key"].(string)),
|
||||||
|
Values: expandStringList(fCfg["values"].([]interface{})),
|
||||||
|
}
|
||||||
|
|
||||||
|
filters = append(filters, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
filterGroup := &ssm.PatchFilterGroup{
|
||||||
|
PatchFilters: filters,
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := &ssm.PatchRule{
|
||||||
|
ApproveAfterDays: aws.Int64(int64(rCfg["approve_after_days"].(int))),
|
||||||
|
PatchFilterGroup: filterGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
rules = append(rules, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ssm.PatchRuleGroup{
|
||||||
|
PatchRules: rules,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenAwsSsmPatchRuleGroup(group *ssm.PatchRuleGroup) []map[string]interface{} {
|
||||||
|
if len(group.PatchRules) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]map[string]interface{}, 0, len(group.PatchRules))
|
||||||
|
|
||||||
|
for _, rule := range group.PatchRules {
|
||||||
|
r := make(map[string]interface{})
|
||||||
|
r["approve_after_days"] = *rule.ApproveAfterDays
|
||||||
|
r["patch_filter"] = flattenAwsSsmPatchFilterGroup(rule.PatchFilterGroup)
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSSSMPatchBaseline_basic(t *testing.T) {
|
||||||
|
name := acctest.RandString(10)
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSSSMPatchBaselineDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccAWSSSMPatchBaselineBasicConfig(name),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSSSMPatchBaselineExists("aws_ssm_patch_baseline.foo"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_ssm_patch_baseline.foo", "approved_patches.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_ssm_patch_baseline.foo", "approved_patches.2062620480", "KB123456"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_ssm_patch_baseline.foo", "name", fmt.Sprintf("patch-baseline-%s", name)),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccAWSSSMPatchBaselineBasicConfigUpdated(name),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSSSMPatchBaselineExists("aws_ssm_patch_baseline.foo"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_ssm_patch_baseline.foo", "approved_patches.#", "2"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_ssm_patch_baseline.foo", "approved_patches.2062620480", "KB123456"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_ssm_patch_baseline.foo", "approved_patches.2291496788", "KB456789"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_ssm_patch_baseline.foo", "name", fmt.Sprintf("updated-patch-baseline-%s", name)),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSSSMPatchBaselineExists(n string) 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 := testAccProvider.Meta().(*AWSClient).ssmconn
|
||||||
|
|
||||||
|
resp, err := conn.DescribePatchBaselines(&ssm.DescribePatchBaselinesInput{
|
||||||
|
Filters: []*ssm.PatchOrchestratorFilter{
|
||||||
|
{
|
||||||
|
Key: aws.String("NAME_PREFIX"),
|
||||||
|
Values: []*string{aws.String(rs.Primary.Attributes["name"])},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, i := range resp.BaselineIdentities {
|
||||||
|
if *i.BaselineId == rs.Primary.ID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("No AWS SSM Patch Baseline found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSSSMPatchBaselineDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).ssmconn
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_ssm_patch_baseline" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := conn.DescribePatchBaselines(&ssm.DescribePatchBaselinesInput{
|
||||||
|
Filters: []*ssm.PatchOrchestratorFilter{
|
||||||
|
{
|
||||||
|
Key: aws.String("NAME_PREFIX"),
|
||||||
|
Values: []*string{aws.String(rs.Primary.Attributes["name"])},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(out.BaselineIdentities) > 0 {
|
||||||
|
return fmt.Errorf("Expected AWS SSM Patch Baseline to be gone, but was still found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccAWSSSMPatchBaselineBasicConfig(rName string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
|
||||||
|
resource "aws_ssm_patch_baseline" "foo" {
|
||||||
|
name = "patch-baseline-%s"
|
||||||
|
approved_patches = ["KB123456"]
|
||||||
|
}
|
||||||
|
|
||||||
|
`, rName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccAWSSSMPatchBaselineBasicConfigUpdated(rName string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
|
||||||
|
resource "aws_ssm_patch_baseline" "foo" {
|
||||||
|
name = "updated-patch-baseline-%s"
|
||||||
|
approved_patches = ["KB123456","KB456789"]
|
||||||
|
}
|
||||||
|
|
||||||
|
`, rName)
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsSsmPatchGroup() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsSsmPatchGroupCreate,
|
||||||
|
Read: resourceAwsSsmPatchGroupRead,
|
||||||
|
Delete: resourceAwsSsmPatchGroupDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"baseline_id": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"patch_group": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsSsmPatchGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
ssmconn := meta.(*AWSClient).ssmconn
|
||||||
|
|
||||||
|
params := &ssm.RegisterPatchBaselineForPatchGroupInput{
|
||||||
|
BaselineId: aws.String(d.Get("baseline_id").(string)),
|
||||||
|
PatchGroup: aws.String(d.Get("patch_group").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ssmconn.RegisterPatchBaselineForPatchGroup(params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(*resp.PatchGroup)
|
||||||
|
return resourceAwsSsmPatchGroupRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsSsmPatchGroupRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
ssmconn := meta.(*AWSClient).ssmconn
|
||||||
|
|
||||||
|
params := &ssm.DescribePatchGroupsInput{}
|
||||||
|
|
||||||
|
resp, err := ssmconn.DescribePatchGroups(params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, t := range resp.Mappings {
|
||||||
|
if *t.PatchGroup == d.Id() {
|
||||||
|
found = true
|
||||||
|
|
||||||
|
d.Set("patch_group", t.PatchGroup)
|
||||||
|
d.Set("baseline_id", t.BaselineIdentity.BaselineId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
log.Printf("[INFO] Patch Group not found. Removing from state")
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsSsmPatchGroupDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
ssmconn := meta.(*AWSClient).ssmconn
|
||||||
|
|
||||||
|
log.Printf("[INFO] Deleting SSM Patch Group: %s", d.Id())
|
||||||
|
|
||||||
|
params := &ssm.DeregisterPatchBaselineForPatchGroupInput{
|
||||||
|
BaselineId: aws.String(d.Get("baseline_id").(string)),
|
||||||
|
PatchGroup: aws.String(d.Get("patch_group").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := ssmconn.DeregisterPatchBaselineForPatchGroup(params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSSSMPatchGroup_basic(t *testing.T) {
|
||||||
|
name := acctest.RandString(10)
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSSSMPatchGroupDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccAWSSSMPatchGroupBasicConfig(name),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSSSMPatchGroupExists("aws_ssm_patch_group.patchgroup"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSSSMPatchGroupExists(n string) 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 := testAccProvider.Meta().(*AWSClient).ssmconn
|
||||||
|
|
||||||
|
resp, err := conn.DescribePatchGroups(&ssm.DescribePatchGroupsInput{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, i := range resp.Mappings {
|
||||||
|
if *i.BaselineIdentity.BaselineId == rs.Primary.Attributes["baseline_id"] && *i.PatchGroup == rs.Primary.ID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("No AWS SSM Patch Group found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSSSMPatchGroupDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).ssmconn
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_ssm_patch_group" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := conn.DescribePatchGroups(&ssm.DescribePatchGroupsInput{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Verify the error is what we want
|
||||||
|
if ae, ok := err.(awserr.Error); ok && ae.Code() == "DoesNotExistException" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, i := range resp.Mappings {
|
||||||
|
if *i.BaselineIdentity.BaselineId == rs.Primary.Attributes["baseline_id"] && *i.PatchGroup == rs.Primary.ID {
|
||||||
|
return fmt.Errorf("Expected AWS SSM Patch Group to be gone, but was still found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccAWSSSMPatchGroupBasicConfig(rName string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
|
||||||
|
resource "aws_ssm_patch_baseline" "foo" {
|
||||||
|
name = "patch-baseline-%s"
|
||||||
|
approved_patches = ["KB123456"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_ssm_patch_group" "patchgroup" {
|
||||||
|
baseline_id = "${aws_ssm_patch_baseline.foo.id}"
|
||||||
|
patch_group = "patch-group"
|
||||||
|
}
|
||||||
|
|
||||||
|
`, rName)
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
---
|
||||||
|
layout: "aws"
|
||||||
|
page_title: "AWS: aws_ssm_patch_baseline"
|
||||||
|
sidebar_current: "docs-aws-resource-ssm-patch-baseline"
|
||||||
|
description: |-
|
||||||
|
Provides an SSM Patch Baseline resource
|
||||||
|
---
|
||||||
|
|
||||||
|
# aws_ssm_patch_baseline
|
||||||
|
|
||||||
|
Provides an SSM Patch Baseline resource
|
||||||
|
|
||||||
|
~> **NOTE on Patch Baselines:** The `approved_patches` and `approval_rule` are
|
||||||
|
both marked as optional fields, but the Patch Baseline requires that at least one
|
||||||
|
of them is specified.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
Basic usage using `approved_patches` only
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "aws_ssm_patch_baseline" "production" {
|
||||||
|
name = "patch-baseline"
|
||||||
|
approved_patches = ["KB123456"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Advanced usage, specifying patch filters
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "aws_ssm_patch_baseline" "production" {
|
||||||
|
name = "patch-baseline"
|
||||||
|
description = "Patch Baseline Description"
|
||||||
|
approved_patches = ["KB123456", "KB456789"]
|
||||||
|
rejected_patches = ["KB987654"]
|
||||||
|
global_filter {
|
||||||
|
key = "PRODUCT"
|
||||||
|
values = ["WindowsServer2008"]
|
||||||
|
}
|
||||||
|
global_filter {
|
||||||
|
key = "CLASSIFICATION"
|
||||||
|
values = ["ServicePacks"]
|
||||||
|
}
|
||||||
|
global_filter {
|
||||||
|
key = "MSRC_SEVERITY"
|
||||||
|
values = ["Low"]
|
||||||
|
}
|
||||||
|
approval_rule {
|
||||||
|
approve_after_days = 7
|
||||||
|
patch_filter {
|
||||||
|
key = "PRODUCT"
|
||||||
|
values = ["WindowsServer2016"]
|
||||||
|
}
|
||||||
|
patch_filter {
|
||||||
|
key = "CLASSIFICATION"
|
||||||
|
values = ["CriticalUpdates", "SecurityUpdates", "Updates"]
|
||||||
|
}
|
||||||
|
patch_filter {
|
||||||
|
key = "MSRC_SEVERITY"
|
||||||
|
values = ["Critical", "Important", "Moderate"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
approval_rule {
|
||||||
|
approve_after_days = 7
|
||||||
|
patch_filter {
|
||||||
|
key = "PRODUCT"
|
||||||
|
values = ["WindowsServer2012"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `name` - (Required) The name of the patch baseline.
|
||||||
|
* `description` - (Optional) The description of the patch baseline.
|
||||||
|
* `approved_patches` - (Optional) A list of explicitly approved patches for the baseline.
|
||||||
|
* `rejected_patches` - (Optional) A list of rejected patches.
|
||||||
|
* `global_filter` - (Optional) A set of global filters used to exclude patches from the baseline. Up to 4 global filters can be specified using Key/Value pairs. Valid Keys are `PRODUCT | CLASSIFICATION | MSRC_SEVERITY | PATCH_ID`.
|
||||||
|
* `approval_rule` - (Optional) A set of rules used to include patches in the baseline. up to 10 approval rules can be specified. Each approval_rule block requires the fields documented below.
|
||||||
|
|
||||||
|
The `approval_rule` block supports:
|
||||||
|
|
||||||
|
* `approve_after_days` - (Required) The number of days after the release date of each patch matched by the rule the patch is marked as approved in the patch baseline. Valid Range: 0 to 100.
|
||||||
|
* `patch_filter` - (Required) The patch filter group that defines the criteria for the rule. Up to 4 patch filters can be specified per approval rule using Key/Value pairs. Valid Keys are `PRODUCT | CLASSIFICATION | MSRC_SEVERITY | PATCH_ID`.
|
||||||
|
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The ID of the patch baseline.
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
layout: "aws"
|
||||||
|
page_title: "AWS: aws_ssm_patch_group"
|
||||||
|
sidebar_current: "docs-aws-resource-ssm-patch-group"
|
||||||
|
description: |-
|
||||||
|
Provides an SSM Patch Group resource
|
||||||
|
---
|
||||||
|
|
||||||
|
# aws_ssm_patch_group
|
||||||
|
|
||||||
|
Provides an SSM Patch Group resource
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "aws_ssm_patch_baseline" "production" {
|
||||||
|
name = "patch-baseline"
|
||||||
|
approved_patches = ["KB123456"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_ssm_patch_group" "patchgroup" {
|
||||||
|
baseline_id = "${aws_ssm_patch_baseline.production.id}"
|
||||||
|
patch_group = "patch-group-name"
|
||||||
|
}```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `baseline_id` - (Required) The ID of the patch baseline to register the patch group with.
|
||||||
|
* `patch_group` - (Required) The name of the patch group that should be registered with the patch baseline.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The ID of the patch baseline.
|
Loading…
Reference in New Issue