The aws:ssmcontacts/plan:Plan resource, part of the Pulumi AWS provider, defines escalation plans that control when and how AWS Systems Manager Incident Manager engages contacts during incidents. This guide focuses on three capabilities: escalation timing configuration, multi-contact parallel engagement, and channel routing with retry logic.
Plans reference existing SSM Contacts and contact channels. They define the escalation sequence but don’t create the contacts themselves. The examples are intentionally small. Combine them with your own contact and channel resources.
Define an escalation plan for a contact
Incident response workflows begin by creating a contact and defining when to engage them.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const contact = new aws.ssmcontacts.Contact("contact", {
alias: "alias",
type: "PERSONAL",
});
const plan = new aws.ssmcontacts.Plan("plan", {
contactId: contact.arn,
stages: [{
durationInMinutes: 1,
}],
});
import pulumi
import pulumi_aws as aws
contact = aws.ssmcontacts.Contact("contact",
alias="alias",
type="PERSONAL")
plan = aws.ssmcontacts.Plan("plan",
contact_id=contact.arn,
stages=[{
"duration_in_minutes": 1,
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ssmcontacts"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
contact, err := ssmcontacts.NewContact(ctx, "contact", &ssmcontacts.ContactArgs{
Alias: pulumi.String("alias"),
Type: pulumi.String("PERSONAL"),
})
if err != nil {
return err
}
_, err = ssmcontacts.NewPlan(ctx, "plan", &ssmcontacts.PlanArgs{
ContactId: contact.Arn,
Stages: ssmcontacts.PlanStageArray{
&ssmcontacts.PlanStageArgs{
DurationInMinutes: pulumi.Int(1),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var contact = new Aws.SsmContacts.Contact("contact", new()
{
Alias = "alias",
Type = "PERSONAL",
});
var plan = new Aws.SsmContacts.Plan("plan", new()
{
ContactId = contact.Arn,
Stages = new[]
{
new Aws.SsmContacts.Inputs.PlanStageArgs
{
DurationInMinutes = 1,
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ssmcontacts.Contact;
import com.pulumi.aws.ssmcontacts.ContactArgs;
import com.pulumi.aws.ssmcontacts.Plan;
import com.pulumi.aws.ssmcontacts.PlanArgs;
import com.pulumi.aws.ssmcontacts.inputs.PlanStageArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var contact = new Contact("contact", ContactArgs.builder()
.alias("alias")
.type("PERSONAL")
.build());
var plan = new Plan("plan", PlanArgs.builder()
.contactId(contact.arn())
.stages(PlanStageArgs.builder()
.durationInMinutes(1)
.build())
.build());
}
}
resources:
contact:
type: aws:ssmcontacts:Contact
properties:
alias: alias
type: PERSONAL
plan:
type: aws:ssmcontacts:Plan
properties:
contactId: ${contact.arn}
stages:
- durationInMinutes: 1
The contactId property points to the contact’s ARN. The stages array defines escalation timing: each stage specifies durationInMinutes, which controls how long to wait before moving to the next stage. Here, the plan waits 1 minute before escalating. Without targets specified, the stage engages the contact directly.
Route alerts through multiple contacts and channels
Complex incidents require coordinated notification across teams and communication channels.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const escalationPlan = new aws.ssmcontacts.Contact("escalation_plan", {
alias: "escalation-plan-alias",
type: "ESCALATION",
});
const contactOne = new aws.ssmcontacts.Contact("contact_one", {
alias: "alias",
type: "PERSONAL",
});
const contactTwo = new aws.ssmcontacts.Contact("contact_two", {
alias: "alias",
type: "PERSONAL",
});
const test = new aws.ssmcontacts.Plan("test", {
contactId: escalationPlan.arn,
stages: [{
durationInMinutes: 0,
targets: [
{
contactTargetInfo: {
isEssential: false,
contactId: contactOne.arn,
},
},
{
contactTargetInfo: {
isEssential: true,
contactId: contactTwo.arn,
},
},
{
channelTargetInfo: {
retryIntervalInMinutes: 2,
contactChannelId: channel.arn,
},
},
],
}],
});
import pulumi
import pulumi_aws as aws
escalation_plan = aws.ssmcontacts.Contact("escalation_plan",
alias="escalation-plan-alias",
type="ESCALATION")
contact_one = aws.ssmcontacts.Contact("contact_one",
alias="alias",
type="PERSONAL")
contact_two = aws.ssmcontacts.Contact("contact_two",
alias="alias",
type="PERSONAL")
test = aws.ssmcontacts.Plan("test",
contact_id=escalation_plan.arn,
stages=[{
"duration_in_minutes": 0,
"targets": [
{
"contact_target_info": {
"is_essential": False,
"contact_id": contact_one.arn,
},
},
{
"contact_target_info": {
"is_essential": True,
"contact_id": contact_two.arn,
},
},
{
"channel_target_info": {
"retry_interval_in_minutes": 2,
"contact_channel_id": channel["arn"],
},
},
],
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ssmcontacts"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
escalationPlan, err := ssmcontacts.NewContact(ctx, "escalation_plan", &ssmcontacts.ContactArgs{
Alias: pulumi.String("escalation-plan-alias"),
Type: pulumi.String("ESCALATION"),
})
if err != nil {
return err
}
contactOne, err := ssmcontacts.NewContact(ctx, "contact_one", &ssmcontacts.ContactArgs{
Alias: pulumi.String("alias"),
Type: pulumi.String("PERSONAL"),
})
if err != nil {
return err
}
contactTwo, err := ssmcontacts.NewContact(ctx, "contact_two", &ssmcontacts.ContactArgs{
Alias: pulumi.String("alias"),
Type: pulumi.String("PERSONAL"),
})
if err != nil {
return err
}
_, err = ssmcontacts.NewPlan(ctx, "test", &ssmcontacts.PlanArgs{
ContactId: escalationPlan.Arn,
Stages: ssmcontacts.PlanStageArray{
&ssmcontacts.PlanStageArgs{
DurationInMinutes: pulumi.Int(0),
Targets: ssmcontacts.PlanStageTargetArray{
&ssmcontacts.PlanStageTargetArgs{
ContactTargetInfo: &ssmcontacts.PlanStageTargetContactTargetInfoArgs{
IsEssential: pulumi.Bool(false),
ContactId: contactOne.Arn,
},
},
&ssmcontacts.PlanStageTargetArgs{
ContactTargetInfo: &ssmcontacts.PlanStageTargetContactTargetInfoArgs{
IsEssential: pulumi.Bool(true),
ContactId: contactTwo.Arn,
},
},
&ssmcontacts.PlanStageTargetArgs{
ChannelTargetInfo: &ssmcontacts.PlanStageTargetChannelTargetInfoArgs{
RetryIntervalInMinutes: pulumi.Int(2),
ContactChannelId: pulumi.Any(channel.Arn),
},
},
},
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var escalationPlan = new Aws.SsmContacts.Contact("escalation_plan", new()
{
Alias = "escalation-plan-alias",
Type = "ESCALATION",
});
var contactOne = new Aws.SsmContacts.Contact("contact_one", new()
{
Alias = "alias",
Type = "PERSONAL",
});
var contactTwo = new Aws.SsmContacts.Contact("contact_two", new()
{
Alias = "alias",
Type = "PERSONAL",
});
var test = new Aws.SsmContacts.Plan("test", new()
{
ContactId = escalationPlan.Arn,
Stages = new[]
{
new Aws.SsmContacts.Inputs.PlanStageArgs
{
DurationInMinutes = 0,
Targets = new[]
{
new Aws.SsmContacts.Inputs.PlanStageTargetArgs
{
ContactTargetInfo = new Aws.SsmContacts.Inputs.PlanStageTargetContactTargetInfoArgs
{
IsEssential = false,
ContactId = contactOne.Arn,
},
},
new Aws.SsmContacts.Inputs.PlanStageTargetArgs
{
ContactTargetInfo = new Aws.SsmContacts.Inputs.PlanStageTargetContactTargetInfoArgs
{
IsEssential = true,
ContactId = contactTwo.Arn,
},
},
new Aws.SsmContacts.Inputs.PlanStageTargetArgs
{
ChannelTargetInfo = new Aws.SsmContacts.Inputs.PlanStageTargetChannelTargetInfoArgs
{
RetryIntervalInMinutes = 2,
ContactChannelId = channel.Arn,
},
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ssmcontacts.Contact;
import com.pulumi.aws.ssmcontacts.ContactArgs;
import com.pulumi.aws.ssmcontacts.Plan;
import com.pulumi.aws.ssmcontacts.PlanArgs;
import com.pulumi.aws.ssmcontacts.inputs.PlanStageArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var escalationPlan = new Contact("escalationPlan", ContactArgs.builder()
.alias("escalation-plan-alias")
.type("ESCALATION")
.build());
var contactOne = new Contact("contactOne", ContactArgs.builder()
.alias("alias")
.type("PERSONAL")
.build());
var contactTwo = new Contact("contactTwo", ContactArgs.builder()
.alias("alias")
.type("PERSONAL")
.build());
var test = new Plan("test", PlanArgs.builder()
.contactId(escalationPlan.arn())
.stages(PlanStageArgs.builder()
.durationInMinutes(0)
.targets(
PlanStageTargetArgs.builder()
.contactTargetInfo(PlanStageTargetContactTargetInfoArgs.builder()
.isEssential(false)
.contactId(contactOne.arn())
.build())
.build(),
PlanStageTargetArgs.builder()
.contactTargetInfo(PlanStageTargetContactTargetInfoArgs.builder()
.isEssential(true)
.contactId(contactTwo.arn())
.build())
.build(),
PlanStageTargetArgs.builder()
.channelTargetInfo(PlanStageTargetChannelTargetInfoArgs.builder()
.retryIntervalInMinutes(2)
.contactChannelId(channel.arn())
.build())
.build())
.build())
.build());
}
}
resources:
escalationPlan:
type: aws:ssmcontacts:Contact
name: escalation_plan
properties:
alias: escalation-plan-alias
type: ESCALATION
contactOne:
type: aws:ssmcontacts:Contact
name: contact_one
properties:
alias: alias
type: PERSONAL
contactTwo:
type: aws:ssmcontacts:Contact
name: contact_two
properties:
alias: alias
type: PERSONAL
test:
type: aws:ssmcontacts:Plan
properties:
contactId: ${escalationPlan.arn}
stages:
- durationInMinutes: 0
targets:
- contactTargetInfo:
isEssential: false
contactId: ${contactOne.arn}
- contactTargetInfo:
isEssential: true
contactId: ${contactTwo.arn}
- channelTargetInfo:
retryIntervalInMinutes: 2
contactChannelId: ${channel.arn}
The targets array within a stage enables parallel engagement. Each target can be a contact (via contactTargetInfo) or a channel (via channelTargetInfo). The isEssential flag marks contacts whose acknowledgment is required before the incident is considered handled. For channels, retryIntervalInMinutes controls how often to retry delivery if the initial attempt fails. Setting durationInMinutes to 0 triggers all targets immediately.
Beyond these examples
These snippets focus on specific plan-level features: escalation timing and stage progression, parallel contact engagement with priority marking, and channel routing with retry intervals. They’re intentionally minimal rather than full incident response configurations.
The examples reference pre-existing infrastructure such as SSM Contacts (PERSONAL or ESCALATION type) and contact channels (email, SMS, voice). They focus on configuring the escalation plan rather than provisioning the contacts and channels.
To keep things focused, common plan patterns are omitted, including:
- Multi-stage escalation sequences (multiple stages array entries)
- Conditional engagement based on acknowledgment
- Integration with CloudWatch alarms or EventBridge
- Contact channel configuration (separate resource)
These omissions are intentional: the goal is to illustrate how each plan feature is wired, not provide drop-in incident response modules. See the SSM Contacts Plan resource reference for all available configuration options.
Let's create AWS Systems Manager Contacts Plans
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Setup
contactId is the required ARN that references either a PERSONAL contact or an ESCALATION plan. You can see both types in the examples.contactId is immutable. Changing it requires replacing the entire resource.durationInMinutes that determines how long to wait before moving to the next stage, and targets that specify who or what to contact.Escalation Behavior
durationInMinutes to 0 in your stage configuration for immediate escalation.isEssential flag on contactTargetInfo indicates whether a contact is critical for the escalation. The example shows both true and false values.targets array, including both contacts and channels.Target Types & Channels
contactTargetInfo targets another contact (with an isEssential flag), while channelTargetInfo targets a communication channel and supports retry configuration with retryIntervalInMinutes.retryIntervalInMinutes within channelTargetInfo to specify how long to wait between retry attempts.region property is required but defaults to the region configured in your provider.Using a different cloud?
Explore integration guides for other cloud providers: