Skip to content

Commit 286ff1f

Browse files
committed
support on-call rotations for ssmcontacts
1 parent a17a256 commit 286ff1f

File tree

6 files changed

+236
-8
lines changed

6 files changed

+236
-8
lines changed

internal/service/ssmcontacts/contact.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1616
"github.com/hashicorp/terraform-provider-aws/internal/conns"
1717
"github.com/hashicorp/terraform-provider-aws/internal/create"
18+
"github.com/hashicorp/terraform-provider-aws/internal/flex"
1819
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
1920
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
2021
"github.com/hashicorp/terraform-provider-aws/names"
@@ -49,6 +50,13 @@ func ResourceContact() *schema.Resource {
4950
Type: schema.TypeString,
5051
Optional: true,
5152
},
53+
"rotation_ids": {
54+
Type: schema.TypeList,
55+
Optional: true,
56+
Elem: &schema.Schema{
57+
Type: schema.TypeString,
58+
},
59+
},
5260
names.AttrType: {
5361
Type: schema.TypeString,
5462
Required: true,
@@ -68,12 +76,23 @@ func resourceContactCreate(ctx context.Context, d *schema.ResourceData, meta any
6876
var diags diag.Diagnostics
6977
client := meta.(*conns.AWSClient).SSMContactsClient(ctx)
7078

79+
contactType := types.ContactType(d.Get(names.AttrType).(string))
80+
7181
input := &ssmcontacts.CreateContactInput{
7282
Alias: aws.String(d.Get(names.AttrAlias).(string)),
7383
DisplayName: aws.String(d.Get(names.AttrDisplayName).(string)),
74-
Plan: &types.Plan{Stages: []types.Stage{}},
7584
Tags: getTagsIn(ctx),
76-
Type: types.ContactType(d.Get(names.AttrType).(string)),
85+
Type: contactType,
86+
}
87+
88+
if contactType == types.ContactTypeOncallSchedule {
89+
plan := &types.Plan{}
90+
if v, ok := d.GetOk("rotation_ids"); ok {
91+
plan.RotationIds = flex.ExpandStringValueList(v.([]any))
92+
}
93+
input.Plan = plan
94+
} else {
95+
input.Plan = &types.Plan{Stages: []types.Stage{}}
7796
}
7897

7998
output, err := client.CreateContact(ctx, input)
@@ -117,10 +136,23 @@ func resourceContactUpdate(ctx context.Context, d *schema.ResourceData, meta any
117136
var diags diag.Diagnostics
118137
conn := meta.(*conns.AWSClient).SSMContactsClient(ctx)
119138

120-
if d.HasChanges(names.AttrDisplayName) {
139+
if d.HasChanges(names.AttrDisplayName, "rotation_ids") {
140+
contactType := types.ContactType(d.Get(names.AttrType).(string))
141+
121142
in := &ssmcontacts.UpdateContactInput{
122-
ContactId: aws.String(d.Id()),
123-
DisplayName: aws.String(d.Get(names.AttrDisplayName).(string)),
143+
ContactId: aws.String(d.Id()),
144+
}
145+
146+
if d.HasChange(names.AttrDisplayName) {
147+
in.DisplayName = aws.String(d.Get(names.AttrDisplayName).(string))
148+
}
149+
150+
if d.HasChange("rotation_ids") && contactType == types.ContactTypeOncallSchedule {
151+
plan := &types.Plan{}
152+
if v, ok := d.GetOk("rotation_ids"); ok {
153+
plan.RotationIds = flex.ExpandStringValueList(v.([]any))
154+
}
155+
in.Plan = plan
124156
}
125157

126158
_, err := conn.UpdateContact(ctx, in)

internal/service/ssmcontacts/contact_test.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,156 @@ resource "aws_ssmcontacts_contact" "contact_one" {
383383
}
384384
`, alias, displayName))
385385
}
386+
387+
func testAccContact_oncallSchedule(t *testing.T) {
388+
if testing.Short() {
389+
t.Skip("skipping long-running test in short mode")
390+
}
391+
392+
ctx := acctest.Context(t)
393+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
394+
resourceName := "aws_ssmcontacts_contact.test"
395+
396+
resource.Test(t, resource.TestCase{
397+
PreCheck: func() {
398+
acctest.PreCheck(ctx, t)
399+
testAccContactPreCheck(ctx, t)
400+
},
401+
ErrorCheck: acctest.ErrorCheck(t, names.SSMContactsServiceID),
402+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
403+
CheckDestroy: testAccCheckContactDestroy(ctx),
404+
Steps: []resource.TestStep{
405+
{
406+
Config: testAccContactConfig_oncallSchedule(rName),
407+
Check: resource.ComposeTestCheckFunc(
408+
testAccCheckContactExists(ctx, resourceName),
409+
resource.TestCheckResourceAttr(resourceName, names.AttrAlias, rName),
410+
resource.TestCheckResourceAttr(resourceName, names.AttrType, "ONCALL_SCHEDULE"),
411+
resource.TestCheckResourceAttr(resourceName, "rotation_ids.#", "1"),
412+
acctest.MatchResourceAttrRegionalARN(ctx, resourceName, names.AttrARN, "ssm-contacts", regexache.MustCompile(`contact/.+$`)),
413+
),
414+
},
415+
{
416+
ResourceName: resourceName,
417+
ImportState: true,
418+
ImportStateVerify: true,
419+
},
420+
{
421+
Config: testAccContactConfig_oncallScheduleUpdated(rName),
422+
Check: resource.ComposeTestCheckFunc(
423+
testAccCheckContactExists(ctx, resourceName),
424+
resource.TestCheckResourceAttr(resourceName, names.AttrAlias, rName),
425+
resource.TestCheckResourceAttr(resourceName, names.AttrType, "ONCALL_SCHEDULE"),
426+
resource.TestCheckResourceAttr(resourceName, "rotation_ids.#", "2"),
427+
acctest.MatchResourceAttrRegionalARN(ctx, resourceName, names.AttrARN, "ssm-contacts", regexache.MustCompile(`contact/.+$`)),
428+
),
429+
},
430+
{
431+
Config: testAccContactConfig_none(),
432+
Check: testAccCheckContactDestroy(ctx),
433+
},
434+
},
435+
})
436+
}
437+
438+
func testAccContactConfig_oncallSchedule(alias string) string {
439+
return acctest.ConfigCompose(
440+
testAccContactConfig_base(),
441+
fmt.Sprintf(`
442+
resource "aws_ssmcontacts_contact" "test_contact" {
443+
alias = "%[1]s-contact"
444+
type = "PERSONAL"
445+
446+
depends_on = [aws_ssmincidents_replication_set.test]
447+
}
448+
449+
resource "aws_ssmcontacts_rotation" "test" {
450+
contact_ids = [aws_ssmcontacts_contact.test_contact.arn]
451+
name = %[1]q
452+
recurrence {
453+
number_of_on_calls = 1
454+
recurrence_multiplier = 1
455+
daily_settings {
456+
hour_of_day = 9
457+
minute_of_hour = 0
458+
}
459+
}
460+
time_zone_id = "America/Los_Angeles"
461+
462+
depends_on = [aws_ssmincidents_replication_set.test]
463+
}
464+
465+
resource "aws_ssmcontacts_contact" "test" {
466+
alias = %[1]q
467+
display_name = %[1]q
468+
type = "ONCALL_SCHEDULE"
469+
rotation_ids = [aws_ssmcontacts_rotation.test.arn]
470+
471+
depends_on = [aws_ssmincidents_replication_set.test]
472+
}
473+
`, alias))
474+
}
475+
476+
func testAccContactConfig_oncallScheduleUpdated(alias string) string {
477+
return acctest.ConfigCompose(
478+
testAccContactConfig_base(),
479+
fmt.Sprintf(`
480+
resource "aws_ssmcontacts_contact" "test_contact" {
481+
alias = "%[1]s-contact"
482+
type = "PERSONAL"
483+
484+
depends_on = [aws_ssmincidents_replication_set.test]
485+
}
486+
487+
resource "aws_ssmcontacts_contact" "test_contact_2" {
488+
alias = "%[1]s-contact-2"
489+
type = "PERSONAL"
490+
491+
depends_on = [aws_ssmincidents_replication_set.test]
492+
}
493+
494+
resource "aws_ssmcontacts_rotation" "test" {
495+
contact_ids = [aws_ssmcontacts_contact.test_contact.arn]
496+
name = %[1]q
497+
recurrence {
498+
number_of_on_calls = 1
499+
recurrence_multiplier = 1
500+
daily_settings {
501+
hour_of_day = 9
502+
minute_of_hour = 0
503+
}
504+
}
505+
time_zone_id = "America/Los_Angeles"
506+
507+
depends_on = [aws_ssmincidents_replication_set.test]
508+
}
509+
510+
resource "aws_ssmcontacts_rotation" "test_2" {
511+
contact_ids = [aws_ssmcontacts_contact.test_contact_2.arn]
512+
name = "%[1]s-2"
513+
recurrence {
514+
number_of_on_calls = 1
515+
recurrence_multiplier = 1
516+
daily_settings {
517+
hour_of_day = 14
518+
minute_of_hour = 30
519+
}
520+
}
521+
time_zone_id = "America/New_York"
522+
523+
depends_on = [aws_ssmincidents_replication_set.test]
524+
}
525+
526+
resource "aws_ssmcontacts_contact" "test" {
527+
alias = %[1]q
528+
display_name = %[1]q
529+
type = "ONCALL_SCHEDULE"
530+
rotation_ids = [
531+
aws_ssmcontacts_rotation.test.arn,
532+
aws_ssmcontacts_rotation.test_2.arn
533+
]
534+
535+
depends_on = [aws_ssmincidents_replication_set.test]
536+
}
537+
`, alias))
538+
}

internal/service/ssmcontacts/helper.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ func setContactResourceData(d *schema.ResourceData, getContactOutput *ssmcontact
1616
d.Set(names.AttrAlias, getContactOutput.Alias)
1717
d.Set(names.AttrType, getContactOutput.Type)
1818
d.Set(names.AttrDisplayName, getContactOutput.DisplayName)
19+
if getContactOutput.Plan != nil {
20+
d.Set("rotation_ids", getContactOutput.Plan.RotationIds)
21+
}
1922

2023
return nil
2124
}

internal/service/ssmcontacts/ssmcontacts_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func TestAccSSMContacts_serial(t *testing.T) {
2222
"updateDisplayName": testAccContact_updateDisplayName,
2323
"tags": testAccSSMContactsContact_tagsSerial,
2424
"updateType": testAccContact_updateType,
25+
"oncallSchedule": testAccContact_oncallSchedule,
2526
},
2627
"ContactDataSource": {
2728
acctest.CtBasic: testAccContactDataSource_basic,

website/docs/d/ssmcontacts_contact.html.markdown

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ This data source supports the following arguments:
3232
This data source exports the following attributes in addition to the arguments above:
3333

3434
* `alias` - A unique and identifiable alias of the contact or escalation plan.
35-
* `type` - The type of contact engaged. A single contact is type `PERSONAL` and an escalation plan is type `ESCALATION`.
35+
* `type` - The type of contact engaged. A single contact is type `PERSONAL`, an escalation plan is type `ESCALATION`, and an on-call schedule is type `ONCALL_SCHEDULE`.
3636
* `display_name` - Full friendly name of the contact or escalation plan.
37+
* `rotation_ids` - List of rotation IDs associated with the contact.
3738
* `tags` - Map of tags to assign to the resource.

website/docs/r/ssmcontacts_contact.html.markdown

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,56 @@ resource "aws_ssmcontacts_contact" "example" {
4141
}
4242
```
4343

44+
### On-call Schedule Usage
45+
46+
```terraform
47+
resource "aws_ssmcontacts_contact" "oncall_contact" {
48+
alias = "oncall-contact"
49+
type = "PERSONAL"
50+
51+
depends_on = [aws_ssmincidents_replication_set.example]
52+
}
53+
54+
resource "aws_ssmcontacts_rotation" "example" {
55+
contact_ids = [aws_ssmcontacts_contact.oncall_contact.arn]
56+
name = "example-rotation"
57+
58+
recurrence {
59+
number_of_on_calls = 1
60+
recurrence_multiplier = 1
61+
daily_settings {
62+
hour_of_day = 9
63+
minute_of_hour = 0
64+
}
65+
}
66+
67+
time_zone_id = "America/Los_Angeles"
68+
69+
depends_on = [aws_ssmincidents_replication_set.example]
70+
}
71+
72+
resource "aws_ssmcontacts_contact" "example" {
73+
alias = "oncall-schedule"
74+
display_name = "Example On-call Schedule"
75+
type = "ONCALL_SCHEDULE"
76+
rotation_ids = [aws_ssmcontacts_rotation.example.arn]
77+
78+
depends_on = [aws_ssmincidents_replication_set.example]
79+
}
80+
```
81+
4482
## Argument Reference
4583

4684
The following arguments are required:
4785

4886
- `alias` - (Required) A unique and identifiable alias for the contact or escalation plan. Must be between 1 and 255 characters, and may contain alphanumerics, underscores (`_`), and hyphens (`-`).
49-
- `type` - (Required) The type of contact engaged. A single contact is type PERSONAL and an escalation
50-
plan is type ESCALATION.
87+
- `type` - (Required) The type of contact engaged. A single contact is type `PERSONAL`, an escalation plan is type `ESCALATION`, and an on-call schedule is type `ONCALL_SCHEDULE`.
5188

5289
The following arguments are optional:
5390

5491
- `region` - (Optional) Region where this resource will be [managed](https://docs.aws.amazon.com/general/latest/gr/rande.html#regional-endpoints). Defaults to the Region set in the [provider configuration](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#aws-configuration-reference).
5592
- `display_name` - (Optional) Full friendly name of the contact or escalation plan. If set, must be between 1 and 255 characters, and may contain alphanumerics, underscores (`_`), hyphens (`-`), periods (`.`), and spaces.
93+
- `rotation_ids` - (Optional) List of rotation IDs associated with the contact. Required when `type` is `ONCALL_SCHEDULE`.
5694
- `tags` - (Optional) Key-value tags for the monitor. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.
5795

5896
## Attribute Reference

0 commit comments

Comments
 (0)