The aws:ssm/association:Association resource, part of the Pulumi AWS provider, links SSM documents to EC2 instances or tag groups, defining what runs, where it runs, and when it runs. This guide focuses on three capabilities: instance and tag-based targeting, scheduled execution with cron expressions, and parameter passing with execution controls.
Associations depend on SSM documents (AWS-managed or custom) and EC2 instances with the SSM agent installed and proper IAM permissions. The examples are intentionally small. Combine them with your own documents, instances, and scheduling requirements.
Apply a document to a single instance
Most SSM workflows start by applying a document to a specific instance, such as installing software or running a configuration script.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.ssm.Association("example", {
name: exampleAwsSsmDocument.name,
targets: [{
key: "InstanceIds",
values: [exampleAwsInstance.id],
}],
});
import pulumi
import pulumi_aws as aws
example = aws.ssm.Association("example",
name=example_aws_ssm_document["name"],
targets=[{
"key": "InstanceIds",
"values": [example_aws_instance["id"]],
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ssm"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ssm.NewAssociation(ctx, "example", &ssm.AssociationArgs{
Name: pulumi.Any(exampleAwsSsmDocument.Name),
Targets: ssm.AssociationTargetArray{
&ssm.AssociationTargetArgs{
Key: pulumi.String("InstanceIds"),
Values: pulumi.StringArray{
exampleAwsInstance.Id,
},
},
},
})
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 example = new Aws.Ssm.Association("example", new()
{
Name = exampleAwsSsmDocument.Name,
Targets = new[]
{
new Aws.Ssm.Inputs.AssociationTargetArgs
{
Key = "InstanceIds",
Values = new[]
{
exampleAwsInstance.Id,
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ssm.Association;
import com.pulumi.aws.ssm.AssociationArgs;
import com.pulumi.aws.ssm.inputs.AssociationTargetArgs;
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 example = new Association("example", AssociationArgs.builder()
.name(exampleAwsSsmDocument.name())
.targets(AssociationTargetArgs.builder()
.key("InstanceIds")
.values(exampleAwsInstance.id())
.build())
.build());
}
}
resources:
example:
type: aws:ssm:Association
properties:
name: ${exampleAwsSsmDocument.name}
targets:
- key: InstanceIds
values:
- ${exampleAwsInstance.id}
The name property specifies which SSM document to run. The targets array defines where to run it: set key to “InstanceIds” and values to an array of instance IDs. This creates a one-to-one association between the document and the instance.
Apply a document to all managed instances
Fleet-wide operations like installing monitoring agents or applying security patches often target every managed instance in an account.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.ssm.Association("example", {
name: "AmazonCloudWatch-ManageAgent",
targets: [{
key: "InstanceIds",
values: ["*"],
}],
});
import pulumi
import pulumi_aws as aws
example = aws.ssm.Association("example",
name="AmazonCloudWatch-ManageAgent",
targets=[{
"key": "InstanceIds",
"values": ["*"],
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ssm"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ssm.NewAssociation(ctx, "example", &ssm.AssociationArgs{
Name: pulumi.String("AmazonCloudWatch-ManageAgent"),
Targets: ssm.AssociationTargetArray{
&ssm.AssociationTargetArgs{
Key: pulumi.String("InstanceIds"),
Values: pulumi.StringArray{
pulumi.String("*"),
},
},
},
})
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 example = new Aws.Ssm.Association("example", new()
{
Name = "AmazonCloudWatch-ManageAgent",
Targets = new[]
{
new Aws.Ssm.Inputs.AssociationTargetArgs
{
Key = "InstanceIds",
Values = new[]
{
"*",
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ssm.Association;
import com.pulumi.aws.ssm.AssociationArgs;
import com.pulumi.aws.ssm.inputs.AssociationTargetArgs;
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 example = new Association("example", AssociationArgs.builder()
.name("AmazonCloudWatch-ManageAgent")
.targets(AssociationTargetArgs.builder()
.key("InstanceIds")
.values("*")
.build())
.build());
}
}
resources:
example:
type: aws:ssm:Association
properties:
name: AmazonCloudWatch-ManageAgent
targets:
- key: InstanceIds
values:
- '*'
Setting values to ["*"] with key “InstanceIds” targets all managed instances. This example uses an AWS-managed document (AmazonCloudWatch-ManageAgent) rather than a custom one, showing how to reference documents from AWS’s catalog.
Target instances by tag key and value
Tag-based targeting lets you apply documents to logical groups like all development servers or all instances in a specific application tier.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.ssm.Association("example", {
name: "AmazonCloudWatch-ManageAgent",
targets: [{
key: "tag:Environment",
values: ["Development"],
}],
});
import pulumi
import pulumi_aws as aws
example = aws.ssm.Association("example",
name="AmazonCloudWatch-ManageAgent",
targets=[{
"key": "tag:Environment",
"values": ["Development"],
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ssm"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ssm.NewAssociation(ctx, "example", &ssm.AssociationArgs{
Name: pulumi.String("AmazonCloudWatch-ManageAgent"),
Targets: ssm.AssociationTargetArray{
&ssm.AssociationTargetArgs{
Key: pulumi.String("tag:Environment"),
Values: pulumi.StringArray{
pulumi.String("Development"),
},
},
},
})
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 example = new Aws.Ssm.Association("example", new()
{
Name = "AmazonCloudWatch-ManageAgent",
Targets = new[]
{
new Aws.Ssm.Inputs.AssociationTargetArgs
{
Key = "tag:Environment",
Values = new[]
{
"Development",
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ssm.Association;
import com.pulumi.aws.ssm.AssociationArgs;
import com.pulumi.aws.ssm.inputs.AssociationTargetArgs;
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 example = new Association("example", AssociationArgs.builder()
.name("AmazonCloudWatch-ManageAgent")
.targets(AssociationTargetArgs.builder()
.key("tag:Environment")
.values("Development")
.build())
.build());
}
}
resources:
example:
type: aws:ssm:Association
properties:
name: AmazonCloudWatch-ManageAgent
targets:
- key: tag:Environment
values:
- Development
The key property uses “tag:Environment” syntax to filter by tag. The values array specifies which tag values to match. This runs the document on all instances tagged with Environment=Development, automatically including new instances as they’re tagged.
Schedule recurring document execution
Maintenance tasks like patching or log rotation often run on a schedule rather than immediately at association creation.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.ssm.Association("example", {
name: exampleAwsSsmDocument.name,
scheduleExpression: "cron(0 2 ? * SUN *)",
targets: [{
key: "InstanceIds",
values: [exampleAwsInstance.id],
}],
});
import pulumi
import pulumi_aws as aws
example = aws.ssm.Association("example",
name=example_aws_ssm_document["name"],
schedule_expression="cron(0 2 ? * SUN *)",
targets=[{
"key": "InstanceIds",
"values": [example_aws_instance["id"]],
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ssm"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ssm.NewAssociation(ctx, "example", &ssm.AssociationArgs{
Name: pulumi.Any(exampleAwsSsmDocument.Name),
ScheduleExpression: pulumi.String("cron(0 2 ? * SUN *)"),
Targets: ssm.AssociationTargetArray{
&ssm.AssociationTargetArgs{
Key: pulumi.String("InstanceIds"),
Values: pulumi.StringArray{
exampleAwsInstance.Id,
},
},
},
})
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 example = new Aws.Ssm.Association("example", new()
{
Name = exampleAwsSsmDocument.Name,
ScheduleExpression = "cron(0 2 ? * SUN *)",
Targets = new[]
{
new Aws.Ssm.Inputs.AssociationTargetArgs
{
Key = "InstanceIds",
Values = new[]
{
exampleAwsInstance.Id,
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ssm.Association;
import com.pulumi.aws.ssm.AssociationArgs;
import com.pulumi.aws.ssm.inputs.AssociationTargetArgs;
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 example = new Association("example", AssociationArgs.builder()
.name(exampleAwsSsmDocument.name())
.scheduleExpression("cron(0 2 ? * SUN *)")
.targets(AssociationTargetArgs.builder()
.key("InstanceIds")
.values(exampleAwsInstance.id())
.build())
.build());
}
}
resources:
example:
type: aws:ssm:Association
properties:
name: ${exampleAwsSsmDocument.name}
scheduleExpression: cron(0 2 ? * SUN *)
targets:
- key: InstanceIds
values:
- ${exampleAwsInstance.id}
The scheduleExpression property accepts cron or rate expressions. This example runs every Sunday at 2 AM UTC. Without a schedule, associations run immediately and only once (unless the document itself loops).
Pass parameters and control execution behavior
Complex automation often requires passing shell commands or configuration values to the document, along with controls for concurrency and error handling.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as std from "@pulumi/std";
// First EC2 instance
const webServer1 = new aws.ec2.Instance("web_server_1", {
ami: amazonLinux.id,
instanceType: aws.ec2.InstanceType.T3_Micro,
subnetId: _public.id,
vpcSecurityGroupIds: [ec2Sg.id],
iamInstanceProfile: ec2SsmProfile.name,
userData: `#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
`,
});
// Second EC2 instance
const webServer2 = new aws.ec2.Instance("web_server_2", {
ami: amazonLinux.id,
instanceType: aws.ec2.InstanceType.T3_Micro,
subnetId: _public.id,
vpcSecurityGroupIds: [ec2Sg.id],
iamInstanceProfile: ec2SsmProfile.name,
userData: `#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
`,
});
// Removed EC2 provisioning dependencies for brevity
const systemUpdate = new aws.ssm.Association("system_update", {
name: "AWS-RunShellScript",
targets: [{
key: "InstanceIds",
values: [
webServer1.id,
webServer2.id,
],
}],
scheduleExpression: "cron(0 2 ? * SUN *)",
parameters: {
commands: std.join({
separator: "\n",
input: [
"#!/bin/bash",
"echo 'Starting system update on $(hostname)'",
"echo 'Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)'",
"yum update -y",
"echo 'System update completed successfully'",
"systemctl status httpd",
"df -h",
"free -m",
],
}).then(invoke => invoke.result),
workingDirectory: "/tmp",
executionTimeout: "3600",
},
associationName: "weekly-system-update",
complianceSeverity: "MEDIUM",
maxConcurrency: "1",
maxErrors: "0",
tags: {
Name: "Weekly System Update",
Environment: "demo",
Purpose: "maintenance",
},
});
import pulumi
import pulumi_aws as aws
import pulumi_std as std
# First EC2 instance
web_server1 = aws.ec2.Instance("web_server_1",
ami=amazon_linux["id"],
instance_type=aws.ec2.InstanceType.T3_MICRO,
subnet_id=public["id"],
vpc_security_group_ids=[ec2_sg["id"]],
iam_instance_profile=ec2_ssm_profile["name"],
user_data="""#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
""")
# Second EC2 instance
web_server2 = aws.ec2.Instance("web_server_2",
ami=amazon_linux["id"],
instance_type=aws.ec2.InstanceType.T3_MICRO,
subnet_id=public["id"],
vpc_security_group_ids=[ec2_sg["id"]],
iam_instance_profile=ec2_ssm_profile["name"],
user_data="""#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
""")
# Removed EC2 provisioning dependencies for brevity
system_update = aws.ssm.Association("system_update",
name="AWS-RunShellScript",
targets=[{
"key": "InstanceIds",
"values": [
web_server1.id,
web_server2.id,
],
}],
schedule_expression="cron(0 2 ? * SUN *)",
parameters={
"commands": std.join(separator="\n",
input=[
"#!/bin/bash",
"echo 'Starting system update on $(hostname)'",
"echo 'Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)'",
"yum update -y",
"echo 'System update completed successfully'",
"systemctl status httpd",
"df -h",
"free -m",
]).result,
"workingDirectory": "/tmp",
"executionTimeout": "3600",
},
association_name="weekly-system-update",
compliance_severity="MEDIUM",
max_concurrency="1",
max_errors="0",
tags={
"Name": "Weekly System Update",
"Environment": "demo",
"Purpose": "maintenance",
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ssm"
"github.com/pulumi/pulumi-std/sdk/go/std"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// First EC2 instance
webServer1, err := ec2.NewInstance(ctx, "web_server_1", &ec2.InstanceArgs{
Ami: pulumi.Any(amazonLinux.Id),
InstanceType: pulumi.String(ec2.InstanceType_T3_Micro),
SubnetId: pulumi.Any(public.Id),
VpcSecurityGroupIds: pulumi.StringArray{
ec2Sg.Id,
},
IamInstanceProfile: pulumi.Any(ec2SsmProfile.Name),
UserData: pulumi.String(`#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
`),
})
if err != nil {
return err
}
// Second EC2 instance
webServer2, err := ec2.NewInstance(ctx, "web_server_2", &ec2.InstanceArgs{
Ami: pulumi.Any(amazonLinux.Id),
InstanceType: pulumi.String(ec2.InstanceType_T3_Micro),
SubnetId: pulumi.Any(public.Id),
VpcSecurityGroupIds: pulumi.StringArray{
ec2Sg.Id,
},
IamInstanceProfile: pulumi.Any(ec2SsmProfile.Name),
UserData: pulumi.String(`#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
`),
})
if err != nil {
return err
}
invokeJoin, err := std.Join(ctx, &std.JoinArgs{
Separator: "\n",
Input: []string{
"#!/bin/bash",
"echo 'Starting system update on $(hostname)'",
"echo 'Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)'",
"yum update -y",
"echo 'System update completed successfully'",
"systemctl status httpd",
"df -h",
"free -m",
},
}, nil)
if err != nil {
return err
}
// Removed EC2 provisioning dependencies for brevity
_, err = ssm.NewAssociation(ctx, "system_update", &ssm.AssociationArgs{
Name: pulumi.String("AWS-RunShellScript"),
Targets: ssm.AssociationTargetArray{
&ssm.AssociationTargetArgs{
Key: pulumi.String("InstanceIds"),
Values: pulumi.StringArray{
webServer1.ID(),
webServer2.ID(),
},
},
},
ScheduleExpression: pulumi.String("cron(0 2 ? * SUN *)"),
Parameters: pulumi.StringMap{
"commands": pulumi.String(invokeJoin.Result),
"workingDirectory": pulumi.String("/tmp"),
"executionTimeout": pulumi.String("3600"),
},
AssociationName: pulumi.String("weekly-system-update"),
ComplianceSeverity: pulumi.String("MEDIUM"),
MaxConcurrency: pulumi.String("1"),
MaxErrors: pulumi.String("0"),
Tags: pulumi.StringMap{
"Name": pulumi.String("Weekly System Update"),
"Environment": pulumi.String("demo"),
"Purpose": pulumi.String("maintenance"),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
using Std = Pulumi.Std;
return await Deployment.RunAsync(() =>
{
// First EC2 instance
var webServer1 = new Aws.Ec2.Instance("web_server_1", new()
{
Ami = amazonLinux.Id,
InstanceType = Aws.Ec2.InstanceType.T3_Micro,
SubnetId = @public.Id,
VpcSecurityGroupIds = new[]
{
ec2Sg.Id,
},
IamInstanceProfile = ec2SsmProfile.Name,
UserData = @"#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
",
});
// Second EC2 instance
var webServer2 = new Aws.Ec2.Instance("web_server_2", new()
{
Ami = amazonLinux.Id,
InstanceType = Aws.Ec2.InstanceType.T3_Micro,
SubnetId = @public.Id,
VpcSecurityGroupIds = new[]
{
ec2Sg.Id,
},
IamInstanceProfile = ec2SsmProfile.Name,
UserData = @"#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
",
});
// Removed EC2 provisioning dependencies for brevity
var systemUpdate = new Aws.Ssm.Association("system_update", new()
{
Name = "AWS-RunShellScript",
Targets = new[]
{
new Aws.Ssm.Inputs.AssociationTargetArgs
{
Key = "InstanceIds",
Values = new[]
{
webServer1.Id,
webServer2.Id,
},
},
},
ScheduleExpression = "cron(0 2 ? * SUN *)",
Parameters =
{
{ "commands", Std.Join.Invoke(new()
{
Separator = @"
",
Input = new[]
{
"#!/bin/bash",
"echo 'Starting system update on $(hostname)'",
"echo 'Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)'",
"yum update -y",
"echo 'System update completed successfully'",
"systemctl status httpd",
"df -h",
"free -m",
},
}).Apply(invoke => invoke.Result) },
{ "workingDirectory", "/tmp" },
{ "executionTimeout", "3600" },
},
AssociationName = "weekly-system-update",
ComplianceSeverity = "MEDIUM",
MaxConcurrency = "1",
MaxErrors = "0",
Tags =
{
{ "Name", "Weekly System Update" },
{ "Environment", "demo" },
{ "Purpose", "maintenance" },
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Instance;
import com.pulumi.aws.ec2.InstanceArgs;
import com.pulumi.aws.ssm.Association;
import com.pulumi.aws.ssm.AssociationArgs;
import com.pulumi.aws.ssm.inputs.AssociationTargetArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.JoinArgs;
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) {
// First EC2 instance
var webServer1 = new Instance("webServer1", InstanceArgs.builder()
.ami(amazonLinux.id())
.instanceType("t3.micro")
.subnetId(public_.id())
.vpcSecurityGroupIds(ec2Sg.id())
.iamInstanceProfile(ec2SsmProfile.name())
.userData("""
#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
""")
.build());
// Second EC2 instance
var webServer2 = new Instance("webServer2", InstanceArgs.builder()
.ami(amazonLinux.id())
.instanceType("t3.micro")
.subnetId(public_.id())
.vpcSecurityGroupIds(ec2Sg.id())
.iamInstanceProfile(ec2SsmProfile.name())
.userData("""
#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
""")
.build());
// Removed EC2 provisioning dependencies for brevity
var systemUpdate = new Association("systemUpdate", AssociationArgs.builder()
.name("AWS-RunShellScript")
.targets(AssociationTargetArgs.builder()
.key("InstanceIds")
.values(
webServer1.id(),
webServer2.id())
.build())
.scheduleExpression("cron(0 2 ? * SUN *)")
.parameters(Map.ofEntries(
Map.entry("commands", StdFunctions.join(JoinArgs.builder()
.separator("""
""")
.input(
"#!/bin/bash",
"echo 'Starting system update on $(hostname)'",
"echo 'Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)'",
"yum update -y",
"echo 'System update completed successfully'",
"systemctl status httpd",
"df -h",
"free -m")
.build()).result()),
Map.entry("workingDirectory", "/tmp"),
Map.entry("executionTimeout", "3600")
))
.associationName("weekly-system-update")
.complianceSeverity("MEDIUM")
.maxConcurrency("1")
.maxErrors("0")
.tags(Map.ofEntries(
Map.entry("Name", "Weekly System Update"),
Map.entry("Environment", "demo"),
Map.entry("Purpose", "maintenance")
))
.build());
}
}
resources:
# Removed EC2 provisioning dependencies for brevity
systemUpdate:
type: aws:ssm:Association
name: system_update
properties:
name: AWS-RunShellScript
targets:
- key: InstanceIds
values:
- ${webServer1.id}
- ${webServer2.id}
scheduleExpression: cron(0 2 ? * SUN *)
parameters:
commands:
fn::invoke:
function: std:join
arguments:
separator: |2+
input:
- '#!/bin/bash'
- echo 'Starting system update on $(hostname)'
- 'echo ''Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)'''
- yum update -y
- echo 'System update completed successfully'
- systemctl status httpd
- df -h
- free -m
return: result
workingDirectory: /tmp
executionTimeout: '3600'
associationName: weekly-system-update
complianceSeverity: MEDIUM
maxConcurrency: '1'
maxErrors: '0'
tags:
Name: Weekly System Update
Environment: demo
Purpose: maintenance
# First EC2 instance
webServer1:
type: aws:ec2:Instance
name: web_server_1
properties:
ami: ${amazonLinux.id}
instanceType: t3.micro
subnetId: ${public.id}
vpcSecurityGroupIds:
- ${ec2Sg.id}
iamInstanceProfile: ${ec2SsmProfile.name}
userData: |
#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Second EC2 instance
webServer2:
type: aws:ec2:Instance
name: web_server_2
properties:
ami: ${amazonLinux.id}
instanceType: t3.micro
subnetId: ${public.id}
vpcSecurityGroupIds:
- ${ec2Sg.id}
iamInstanceProfile: ${ec2SsmProfile.name}
userData: |
#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
The parameters property passes configuration to the document. For AWS-RunShellScript, this includes commands to execute, working directory, and timeout. The maxConcurrency and maxErrors properties control how many instances run simultaneously and how many failures are tolerated before stopping execution. The complianceSeverity property sets the severity level for compliance reporting.
Beyond these examples
These snippets focus on specific association-level features: instance and tag-based targeting, scheduling and parameter passing, and concurrency and error controls. They’re intentionally minimal rather than full automation workflows.
The examples reference pre-existing infrastructure such as SSM documents (AWS-managed or custom), EC2 instances with SSM agent installed, and IAM instance profiles with SSM permissions. They focus on configuring the association rather than provisioning the underlying infrastructure.
To keep things focused, common association patterns are omitted, including:
- Output location for storing execution results (outputLocation)
- Change Calendar integration (calendarNames)
- Automation document targeting (automationTargetParameterName)
- Immediate execution control (applyOnlyAtCronInterval)
These omissions are intentional: the goal is to illustrate how each association feature is wired, not provide drop-in automation modules. See the SSM Association resource reference for all available configuration options.
Let's configure AWS Systems Manager Associations
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Targeting & Selection
You have three targeting options:
- Specific instances - Set
targetswithkey: "InstanceIds"and an array of instance IDs invalues - All managed instances - Use
key: "InstanceIds"withvalues: ["*"] - Tag-based targeting - Use
key: "tag:TagName"with desired tag values invalues(e.g.,key: "tag:Environment",values: ["Development"])
Scheduling & Execution
scheduleExpression with a cron or rate expression. For example, cron(0 2 ? * SUN *) runs every Sunday at 2 AM.applyOnlyAtCronInterval to true to prevent immediate execution (note: this isn’t supported for rate expressions).calendarNames to specify one or more Systems Manager Change Calendar names. The association will only run when the Change Calendar is open.Error Handling & Concurrency
maxConcurrency to specify a number (e.g., "10") or percentage (e.g., "10%") of targets allowed to run at the same time.maxErrors to a number or percentage. For example, "3" stops after the fourth error, while "10%" for 50 associations stops after the sixth error.waitForSuccessTimeoutSeconds to specify how many seconds to wait for Success status. The create operation will fail if this timeout is reached without success.Configuration & Lifecycle
name property is immutable, so changing it forces replacement of the entire association.