Skip to content

Commit

Permalink
Merge pull request #23756 from DrFaust92/ssm-maint-win
Browse files Browse the repository at this point in the history
r/ssm_maintenance_window_task - allow not setting `targets`
  • Loading branch information
ewbankkit committed Mar 21, 2022
2 parents 8232890 + a3d4c55 commit 214e58c
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 18 deletions.
11 changes: 11 additions & 0 deletions .changelog/23756.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```release-note:enhancement
resource/aws_ssm_maintenance_window_task: Add `arn` and `window_task_id` attributes.
```

```release-note:enhancement
resource/aws_ssm_maintenance_window_task: Add `cutoff_behavior` argument.
```

```release-note:bug
resource/aws_ssm_maintenance_window_task: Allow creating a window taks without targets.
```
85 changes: 70 additions & 15 deletions internal/service/ssm/maintenance_window_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"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/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand All @@ -28,21 +29,37 @@ func ResourceMaintenanceWindowTask() *schema.Resource {
},

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"window_task_id": {
Type: schema.TypeString,
Computed: true,
},
"window_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"cutoff_behavior": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice(ssm.MaintenanceWindowTaskCutoffBehavior_Values(), false),
},

"max_concurrency": {
Type: schema.TypeString,
Required: true,
Optional: true,
Computed: true,
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^([1-9][0-9]*|[1-9][0-9]%|[1-9]%|100%)$`), "must be a number without leading zeros or a percentage between 1% and 100% without leading zeros and ending with the percentage symbol"),
},

"max_errors": {
Type: schema.TypeString,
Required: true,
Optional: true,
Computed: true,
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^([1-9][0-9]*|[0]|[1-9][0-9]%|[0-9]%|100%)$`), "must be zero, a number without leading zeros, or a percentage between 1% and 100% without leading zeros and ending with the percentage symbol"),
},

Expand Down Expand Up @@ -656,11 +673,21 @@ func resourceMaintenanceWindowTaskCreate(d *schema.ResourceData, meta interface{
log.Printf("[INFO] Registering SSM Maintenance Window Task")

params := &ssm.RegisterTaskWithMaintenanceWindowInput{
WindowId: aws.String(d.Get("window_id").(string)),
MaxConcurrency: aws.String(d.Get("max_concurrency").(string)),
MaxErrors: aws.String(d.Get("max_errors").(string)),
TaskType: aws.String(d.Get("task_type").(string)),
TaskArn: aws.String(d.Get("task_arn").(string)),
WindowId: aws.String(d.Get("window_id").(string)),
TaskType: aws.String(d.Get("task_type").(string)),
TaskArn: aws.String(d.Get("task_arn").(string)),
}

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

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

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

if v, ok := d.GetOk("targets"); ok {
Expand Down Expand Up @@ -715,7 +742,9 @@ func resourceMaintenanceWindowTaskRead(d *schema.ResourceData, meta interface{})
return fmt.Errorf("Error getting Maintenance Window (%s) Task (%s): %s", windowID, d.Id(), err)
}

windowTaskID := aws.StringValue(resp.WindowTaskId)
d.Set("window_id", resp.WindowId)
d.Set("window_task_id", windowTaskID)
d.Set("max_concurrency", resp.MaxConcurrency)
d.Set("max_errors", resp.MaxErrors)
d.Set("task_type", resp.TaskType)
Expand All @@ -724,6 +753,7 @@ func resourceMaintenanceWindowTaskRead(d *schema.ResourceData, meta interface{})
d.Set("priority", resp.Priority)
d.Set("name", resp.Name)
d.Set("description", resp.Description)
d.Set("cutoff_behavior", resp.CutoffBehavior)

if resp.TaskInvocationParameters != nil {
if err := d.Set("task_invocation_parameters", flattenTaskInvocationParameters(resp.TaskInvocationParameters)); err != nil {
Expand All @@ -735,6 +765,15 @@ func resourceMaintenanceWindowTaskRead(d *schema.ResourceData, meta interface{})
return fmt.Errorf("Error setting targets error: %#v", err)
}

arn := arn.ARN{
Partition: meta.(*conns.AWSClient).Partition,
Service: "ssm",
Region: meta.(*conns.AWSClient).Region,
AccountID: meta.(*conns.AWSClient).AccountID,
Resource: fmt.Sprintf("windowtask/%s", windowTaskID),
}.String()
d.Set("arn", arn)

return nil
}

Expand All @@ -743,20 +782,36 @@ func resourceMaintenanceWindowTaskUpdate(d *schema.ResourceData, meta interface{
windowID := d.Get("window_id").(string)

params := &ssm.UpdateMaintenanceWindowTaskInput{
Priority: aws.Int64(int64(d.Get("priority").(int))),
WindowId: aws.String(windowID),
WindowTaskId: aws.String(d.Id()),
MaxConcurrency: aws.String(d.Get("max_concurrency").(string)),
MaxErrors: aws.String(d.Get("max_errors").(string)),
TaskArn: aws.String(d.Get("task_arn").(string)),
Targets: expandTargets(d.Get("targets").([]interface{})),
Replace: aws.Bool(true),
Priority: aws.Int64(int64(d.Get("priority").(int))),
WindowId: aws.String(windowID),
WindowTaskId: aws.String(d.Id()),
TaskArn: aws.String(d.Get("task_arn").(string)),
Replace: aws.Bool(true),
}

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

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

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

if v, ok := d.GetOk("targets"); ok {
params.Targets = expandTargets(v.([]interface{}))
} else {
params.MaxConcurrency = nil
params.MaxErrors = nil
}

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

if v, ok := d.GetOk("name"); ok {
params.Name = aws.String(v.(string))
}
Expand Down
95 changes: 95 additions & 0 deletions internal/service/ssm/maintenance_window_task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ssm_test

import (
"fmt"
"regexp"
"testing"

"github.com/aws/aws-sdk-go/aws"
Expand Down Expand Up @@ -30,6 +31,10 @@ func TestAccSSMMaintenanceWindowTask_basic(t *testing.T) {
Config: testAccMaintenanceWindowTaskBasicConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckMaintenanceWindowTaskExists(resourceName, &before),
acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "ssm", regexp.MustCompile(`windowtask/.+`)),
resource.TestCheckResourceAttrSet(resourceName, "window_task_id"),
resource.TestCheckResourceAttrPair(resourceName, "window_id", "aws_ssm_maintenance_window.test", "id"),
resource.TestCheckResourceAttr(resourceName, "targets.#", "1"),
),
},
{
Expand All @@ -56,6 +61,69 @@ func TestAccSSMMaintenanceWindowTask_basic(t *testing.T) {
})
}

func TestAccSSMMaintenanceWindowTask_noTarget(t *testing.T) {
var before ssm.MaintenanceWindowTask
resourceName := "aws_ssm_maintenance_window_task.test"

rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, ssm.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckMaintenanceWindowTaskDestroy,
Steps: []resource.TestStep{
{
Config: testAccMaintenanceWindowTaskNoTargetConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckMaintenanceWindowTaskExists(resourceName, &before),
resource.TestCheckResourceAttr(resourceName, "targets.#", "0"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateIdFunc: testAccMaintenanceWindowTaskImportStateIdFunc(resourceName),
ImportStateVerify: true,
},
},
})
}

func TestAccSSMMaintenanceWindowTask_cutoff(t *testing.T) {
var before ssm.MaintenanceWindowTask
resourceName := "aws_ssm_maintenance_window_task.test"

rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, ssm.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckMaintenanceWindowTaskDestroy,
Steps: []resource.TestStep{
{
Config: testAccMaintenanceWindowTaskCutoffConfig(rName, "CANCEL_TASK"),
Check: resource.ComposeTestCheckFunc(
testAccCheckMaintenanceWindowTaskExists(resourceName, &before),
resource.TestCheckResourceAttr(resourceName, "cutoff_behavior", "CANCEL_TASK"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateIdFunc: testAccMaintenanceWindowTaskImportStateIdFunc(resourceName),
ImportStateVerify: true,
},
{
Config: testAccMaintenanceWindowTaskCutoffConfig(rName, "CONTINUE_TASK"),
Check: resource.ComposeTestCheckFunc(
testAccCheckMaintenanceWindowTaskExists(resourceName, &before),
resource.TestCheckResourceAttr(resourceName, "cutoff_behavior", "CONTINUE_TASK"),
),
},
},
})
}

func TestAccSSMMaintenanceWindowTask_noRole(t *testing.T) {
var task ssm.MaintenanceWindowTask
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
Expand Down Expand Up @@ -562,6 +630,33 @@ resource "aws_ssm_maintenance_window_task" "test" {
`)
}

func testAccMaintenanceWindowTaskNoTargetConfig(rName string) string {
return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName) + `
resource "aws_ssm_maintenance_window_task" "test" {
window_id = aws_ssm_maintenance_window.test.id
task_type = "AUTOMATION"
task_arn = "AWS-RunShellScript"
priority = 1
service_role_arn = aws_iam_role.test.arn
}
`)
}

func testAccMaintenanceWindowTaskCutoffConfig(rName, cutoff string) string {
return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName)+`
resource "aws_ssm_maintenance_window_task" "test" {
window_id = aws_ssm_maintenance_window.test.id
task_type = "AUTOMATION"
task_arn = "AWS-RunShellScript"
priority = 1
service_role_arn = aws_iam_role.test.arn
cutoff_behavior = %[1]q
}
`, cutoff)
}

func testAccMaintenanceWindowTaskBasicUpdateConfig(rName, description, taskType, taskArn string, priority, maxConcurrency, maxErrors int) string {
return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName)+`
Expand Down
9 changes: 6 additions & 3 deletions website/docs/r/ssm_maintenance_window_task.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,15 @@ resource "aws_ssm_maintenance_window_task" "example" {
The following arguments are supported:

* `window_id` - (Required) The Id of the maintenance window to register the task with.
* `max_concurrency` - (Required) The maximum number of targets this task can be run for in parallel.
* `max_errors` - (Required) The maximum number of errors allowed before this task stops being scheduled.
* `max_concurrency` - (Optional) The maximum number of targets this task can be run for in parallel.
* `max_errors` - (Optional) The maximum number of errors allowed before this task stops being scheduled.
* `cutoff_behavior` - (Optional) Indicates whether tasks should continue to run after the cutoff time specified in the maintenance windows is reached. Valid values are `CONTINUE_TASK` and `CANCEL_TASK`.
* `task_type` - (Required) The type of task being registered. Valid values: `AUTOMATION`, `LAMBDA`, `RUN_COMMAND` or `STEP_FUNCTIONS`.
* `task_arn` - (Required) The ARN of the task to execute.
* `service_role_arn` - (Optional) The role that should be assumed when executing the task. If a role is not provided, Systems Manager uses your account's service-linked role. If no service-linked role for Systems Manager exists in your account, it is created for you.
* `name` - (Optional) The name of the maintenance window task.
* `description` - (Optional) The description of the maintenance window task.
* `targets` - (Required) The targets (either instances or window target ids). Instances are specified using Key=InstanceIds,Values=instanceid1,instanceid2. Window target ids are specified using Key=WindowTargetIds,Values=window target id1, window target id2.
* `targets` - (Optional) The targets (either instances or window target ids). Instances are specified using Key=InstanceIds,Values=instanceid1,instanceid2. Window target ids are specified using Key=WindowTargetIds,Values=window target id1, window target id2.
* `priority` - (Optional) The priority of the task in the Maintenance Window, the lower the number the higher the priority. Tasks in a Maintenance Window are scheduled in priority order with tasks that have the same priority scheduled in parallel.
* `task_invocation_parameters` - (Optional) Configuration block with parameters for task execution.

Expand Down Expand Up @@ -201,7 +202,9 @@ The following arguments are supported:

In addition to all arguments above, the following attributes are exported:

* `arn` - The ARN of the maintenance window task.
* `id` - The ID of the maintenance window task.
* `window_task_id` - The ID of the maintenance window task.

## Import

Expand Down

0 comments on commit 214e58c

Please sign in to comment.