Skip to content

Commit 48d6187

Browse files
committed
support on-call rotations for ssmcontacts
1 parent a17a256 commit 48d6187

File tree

6 files changed

+237
-8
lines changed

6 files changed

+237
-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: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,58 @@ func testAccContact_updateDisplayName(t *testing.T) {
241241
})
242242
}
243243

244+
func testAccContact_oncallSchedule(t *testing.T) {
245+
if testing.Short() {
246+
t.Skip("skipping long-running test in short mode")
247+
}
248+
249+
ctx := acctest.Context(t)
250+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
251+
resourceName := "aws_ssmcontacts_contact.test"
252+
253+
resource.Test(t, resource.TestCase{
254+
PreCheck: func() {
255+
acctest.PreCheck(ctx, t)
256+
testAccContactPreCheck(ctx, t)
257+
},
258+
ErrorCheck: acctest.ErrorCheck(t, names.SSMContactsServiceID),
259+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
260+
CheckDestroy: testAccCheckContactDestroy(ctx),
261+
Steps: []resource.TestStep{
262+
{
263+
Config: testAccContactConfig_oncallSchedule(rName),
264+
Check: resource.ComposeTestCheckFunc(
265+
testAccCheckContactExists(ctx, resourceName),
266+
resource.TestCheckResourceAttr(resourceName, names.AttrAlias, rName),
267+
resource.TestCheckResourceAttr(resourceName, names.AttrType, "ONCALL_SCHEDULE"),
268+
resource.TestCheckResourceAttr(resourceName, "rotation_ids.#", "1"),
269+
acctest.MatchResourceAttrRegionalARN(ctx, resourceName, names.AttrARN, "ssm-contacts", regexache.MustCompile(`contact/.+$`)),
270+
),
271+
},
272+
{
273+
ResourceName: resourceName,
274+
ImportState: true,
275+
ImportStateVerify: true,
276+
},
277+
{
278+
Config: testAccContactConfig_oncallScheduleUpdated(rName),
279+
Check: resource.ComposeTestCheckFunc(
280+
testAccCheckContactExists(ctx, resourceName),
281+
resource.TestCheckResourceAttr(resourceName, names.AttrAlias, rName),
282+
resource.TestCheckResourceAttr(resourceName, names.AttrType, "ONCALL_SCHEDULE"),
283+
resource.TestCheckResourceAttr(resourceName, "rotation_ids.#", "2"),
284+
acctest.MatchResourceAttrRegionalARN(ctx, resourceName, names.AttrARN, "ssm-contacts", regexache.MustCompile(`contact/.+$`)),
285+
),
286+
},
287+
{
288+
Config: testAccContactConfig_none(),
289+
Check: testAccCheckContactDestroy(ctx),
290+
},
291+
},
292+
})
293+
}
294+
295+
244296
func testAccCheckContactDestroy(ctx context.Context) resource.TestCheckFunc {
245297
return func(s *terraform.State) error {
246298
conn := acctest.Provider.Meta().(*conns.AWSClient).SSMContactsClient(ctx)
@@ -383,3 +435,105 @@ resource "aws_ssmcontacts_contact" "contact_one" {
383435
}
384436
`, alias, displayName))
385437
}
438+
439+
func testAccContactConfig_oncallSchedule(alias string) string {
440+
return acctest.ConfigCompose(
441+
testAccContactConfig_base(),
442+
fmt.Sprintf(`
443+
resource "aws_ssmcontacts_contact" "test_contact" {
444+
alias = "%[1]s-contact"
445+
type = "PERSONAL"
446+
447+
depends_on = [aws_ssmincidents_replication_set.test]
448+
}
449+
450+
resource "aws_ssmcontacts_rotation" "test" {
451+
contact_ids = [aws_ssmcontacts_contact.test_contact.arn]
452+
name = %[1]q
453+
recurrence {
454+
number_of_on_calls = 1
455+
recurrence_multiplier = 1
456+
daily_settings {
457+
hour_of_day = 9
458+
minute_of_hour = 0
459+
}
460+
}
461+
time_zone_id = "America/Los_Angeles"
462+
463+
depends_on = [aws_ssmincidents_replication_set.test]
464+
}
465+
466+
resource "aws_ssmcontacts_contact" "test" {
467+
alias = %[1]q
468+
display_name = %[1]q
469+
type = "ONCALL_SCHEDULE"
470+
rotation_ids = [aws_ssmcontacts_rotation.test.arn]
471+
472+
depends_on = [aws_ssmincidents_replication_set.test]
473+
}
474+
`, alias))
475+
}
476+
477+
func testAccContactConfig_oncallScheduleUpdated(alias string) string {
478+
return acctest.ConfigCompose(
479+
testAccContactConfig_base(),
480+
fmt.Sprintf(`
481+
resource "aws_ssmcontacts_contact" "test_contact" {
482+
alias = "%[1]s-contact"
483+
type = "PERSONAL"
484+
485+
depends_on = [aws_ssmincidents_replication_set.test]
486+
}
487+
488+
resource "aws_ssmcontacts_contact" "test_contact_2" {
489+
alias = "%[1]s-contact-2"
490+
type = "PERSONAL"
491+
492+
depends_on = [aws_ssmincidents_replication_set.test]
493+
}
494+
495+
resource "aws_ssmcontacts_rotation" "test" {
496+
contact_ids = [aws_ssmcontacts_contact.test_contact.arn]
497+
name = %[1]q
498+
recurrence {
499+
number_of_on_calls = 1
500+
recurrence_multiplier = 1
501+
daily_settings {
502+
hour_of_day = 9
503+
minute_of_hour = 0
504+
}
505+
}
506+
time_zone_id = "America/Los_Angeles"
507+
508+
depends_on = [aws_ssmincidents_replication_set.test]
509+
}
510+
511+
resource "aws_ssmcontacts_rotation" "test_2" {
512+
contact_ids = [aws_ssmcontacts_contact.test_contact_2.arn]
513+
name = "%[1]s-2"
514+
recurrence {
515+
number_of_on_calls = 1
516+
recurrence_multiplier = 1
517+
daily_settings {
518+
hour_of_day = 14
519+
minute_of_hour = 30
520+
}
521+
}
522+
time_zone_id = "America/New_York"
523+
524+
depends_on = [aws_ssmincidents_replication_set.test]
525+
}
526+
527+
resource "aws_ssmcontacts_contact" "test" {
528+
alias = %[1]q
529+
display_name = %[1]q
530+
type = "ONCALL_SCHEDULE"
531+
rotation_ids = [
532+
aws_ssmcontacts_rotation.test.arn,
533+
aws_ssmcontacts_rotation.test_2.arn
534+
]
535+
536+
depends_on = [aws_ssmincidents_replication_set.test]
537+
}
538+
`, alias))
539+
}

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)