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, and shell script parameters with error 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.
Target a single instance by ID
Most associations start by targeting a specific instance, applying a document to one managed instance.
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 apply. The targets array defines where to apply it: set key to “InstanceIds” and values to an array of instance IDs. This creates a one-to-one association between document and instance.
Target all managed instances in an account
Operations teams often need to apply configuration or monitoring across every managed instance without maintaining individual lists.
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 in your account. This example uses “AmazonCloudWatch-ManageAgent”, an AWS-managed document that configures CloudWatch monitoring. The wildcard approach scales automatically as you add or remove instances.
Target instances by tag key and value
Tag-based targeting applies documents to groups of instances that share common characteristics.
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 format “tag:Environment” tells SSM to match instances by tag. The values array specifies which tag values to include. This association runs on all instances tagged with Environment=Development, making it easy to manage environment-specific configurations.
Schedule associations with cron expressions
Maintenance tasks like patching or updates often run on a schedule rather than immediately.
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. By default, associations also run immediately when created; set applyOnlyAtCronInterval to true to skip the initial run.
Run shell scripts across multiple instances
System maintenance often requires running the same commands across a fleet with control over execution timing 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 inputs to the document. For “AWS-RunShellScript”, the commands parameter accepts shell commands as a string. The maxConcurrency property limits how many instances run simultaneously (“1” means sequential execution). The maxErrors property stops execution after the specified number of failures. The complianceSeverity property categorizes the association for compliance reporting.
Target multiple tag values simultaneously
When managing different server types that need the same maintenance, you can target multiple tag values in one association.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as std from "@pulumi/std";
// SSM Association for Webbased Servers
const databaseAssociation = new aws.ssm.Association("database_association", {
name: systemUpdate.name,
targets: [{
key: "tag:Role",
values: [
"WebServer",
"Database",
],
}],
parameters: {
restartServices: "true",
},
scheduleExpression: "cron(0 3 ? * SUN *)",
});
// EC2 Instance 1 - Web Server with "ServerType" tag
const webServer = new aws.ec2.Instance("web_server", {
ami: amazonLinux.id,
instanceType: aws.ec2.InstanceType.T3_Micro,
subnetId: _default.id,
vpcSecurityGroupIds: [ec2Sg.id],
iamInstanceProfile: ec2SsmProfile.name,
userData: std.base64encode({
input: `#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install Apache web server
yum install -y httpd
systemctl enable httpd
systemctl start httpd
echo \"<h1>Web Server - ${prefix}</h1>\" > /var/www/html/index.html
`,
}).then(invoke => invoke.result),
tags: {
Name: `${prefix}-web-server`,
ServerType: "WebServer",
Role: "WebServer",
Environment: environment,
Owner: owner,
},
});
// EC2 Instance 2 - Database Server with "Role" tag
const databaseServer = new aws.ec2.Instance("database_server", {
ami: amazonLinux.id,
instanceType: aws.ec2.InstanceType.T3_Micro,
subnetId: _default.id,
vpcSecurityGroupIds: [ec2Sg.id],
iamInstanceProfile: ec2SsmProfile.name,
userData: std.base64encode({
input: `#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install MySQL
yum install -y mysql-server
systemctl enable mysqld
systemctl start mysqld
`,
}).then(invoke => invoke.result),
tags: {
Name: `${prefix}-database-server`,
Role: "Database",
Environment: environment,
Owner: owner,
},
});
import pulumi
import pulumi_aws as aws
import pulumi_std as std
# SSM Association for Webbased Servers
database_association = aws.ssm.Association("database_association",
name=system_update["name"],
targets=[{
"key": "tag:Role",
"values": [
"WebServer",
"Database",
],
}],
parameters={
"restartServices": "true",
},
schedule_expression="cron(0 3 ? * SUN *)")
# EC2 Instance 1 - Web Server with "ServerType" tag
web_server = aws.ec2.Instance("web_server",
ami=amazon_linux["id"],
instance_type=aws.ec2.InstanceType.T3_MICRO,
subnet_id=default["id"],
vpc_security_group_ids=[ec2_sg["id"]],
iam_instance_profile=ec2_ssm_profile["name"],
user_data=std.base64encode(input=f"""#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install Apache web server
yum install -y httpd
systemctl enable httpd
systemctl start httpd
echo \"<h1>Web Server - {prefix}</h1>\" > /var/www/html/index.html
""").result,
tags={
"Name": f"{prefix}-web-server",
"ServerType": "WebServer",
"Role": "WebServer",
"Environment": environment,
"Owner": owner,
})
# EC2 Instance 2 - Database Server with "Role" tag
database_server = aws.ec2.Instance("database_server",
ami=amazon_linux["id"],
instance_type=aws.ec2.InstanceType.T3_MICRO,
subnet_id=default["id"],
vpc_security_group_ids=[ec2_sg["id"]],
iam_instance_profile=ec2_ssm_profile["name"],
user_data=std.base64encode(input="""#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install MySQL
yum install -y mysql-server
systemctl enable mysqld
systemctl start mysqld
""").result,
tags={
"Name": f"{prefix}-database-server",
"Role": "Database",
"Environment": environment,
"Owner": owner,
})
package main
import (
"fmt"
"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 {
// SSM Association for Webbased Servers
_, err := ssm.NewAssociation(ctx, "database_association", &ssm.AssociationArgs{
Name: pulumi.Any(systemUpdate.Name),
Targets: ssm.AssociationTargetArray{
&ssm.AssociationTargetArgs{
Key: pulumi.String("tag:Role"),
Values: pulumi.StringArray{
pulumi.String("WebServer"),
pulumi.String("Database"),
},
},
},
Parameters: pulumi.StringMap{
"restartServices": pulumi.String("true"),
},
ScheduleExpression: pulumi.String("cron(0 3 ? * SUN *)"),
})
if err != nil {
return err
}
invokeBase64encode, err := std.Base64encode(ctx, &std.Base64encodeArgs{
Input: fmt.Sprintf(`#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install Apache web server
yum install -y httpd
systemctl enable httpd
systemctl start httpd
echo \"<h1>Web Server - %v</h1>\" > /var/www/html/index.html
`, prefix),
}, nil)
if err != nil {
return err
}
// EC2 Instance 1 - Web Server with "ServerType" tag
_, err = ec2.NewInstance(ctx, "web_server", &ec2.InstanceArgs{
Ami: pulumi.Any(amazonLinux.Id),
InstanceType: pulumi.String(ec2.InstanceType_T3_Micro),
SubnetId: pulumi.Any(_default.Id),
VpcSecurityGroupIds: pulumi.StringArray{
ec2Sg.Id,
},
IamInstanceProfile: pulumi.Any(ec2SsmProfile.Name),
UserData: pulumi.String(invokeBase64encode.Result),
Tags: pulumi.StringMap{
"Name": pulumi.Sprintf("%v-web-server", prefix),
"ServerType": pulumi.String("WebServer"),
"Role": pulumi.String("WebServer"),
"Environment": pulumi.Any(environment),
"Owner": pulumi.Any(owner),
},
})
if err != nil {
return err
}
invokeBase64encode1, err := std.Base64encode(ctx, &std.Base64encodeArgs{
Input: `#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install MySQL
yum install -y mysql-server
systemctl enable mysqld
systemctl start mysqld
`,
}, nil)
if err != nil {
return err
}
// EC2 Instance 2 - Database Server with "Role" tag
_, err = ec2.NewInstance(ctx, "database_server", &ec2.InstanceArgs{
Ami: pulumi.Any(amazonLinux.Id),
InstanceType: pulumi.String(ec2.InstanceType_T3_Micro),
SubnetId: pulumi.Any(_default.Id),
VpcSecurityGroupIds: pulumi.StringArray{
ec2Sg.Id,
},
IamInstanceProfile: pulumi.Any(ec2SsmProfile.Name),
UserData: pulumi.String(invokeBase64encode1.Result),
Tags: pulumi.StringMap{
"Name": pulumi.Sprintf("%v-database-server", prefix),
"Role": pulumi.String("Database"),
"Environment": pulumi.Any(environment),
"Owner": pulumi.Any(owner),
},
})
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(() =>
{
// SSM Association for Webbased Servers
var databaseAssociation = new Aws.Ssm.Association("database_association", new()
{
Name = systemUpdate.Name,
Targets = new[]
{
new Aws.Ssm.Inputs.AssociationTargetArgs
{
Key = "tag:Role",
Values = new[]
{
"WebServer",
"Database",
},
},
},
Parameters =
{
{ "restartServices", "true" },
},
ScheduleExpression = "cron(0 3 ? * SUN *)",
});
// EC2 Instance 1 - Web Server with "ServerType" tag
var webServer = new Aws.Ec2.Instance("web_server", new()
{
Ami = amazonLinux.Id,
InstanceType = Aws.Ec2.InstanceType.T3_Micro,
SubnetId = @default.Id,
VpcSecurityGroupIds = new[]
{
ec2Sg.Id,
},
IamInstanceProfile = ec2SsmProfile.Name,
UserData = Std.Base64encode.Invoke(new()
{
Input = @$"#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install Apache web server
yum install -y httpd
systemctl enable httpd
systemctl start httpd
echo \""<h1>Web Server - {prefix}</h1>\"" > /var/www/html/index.html
",
}).Apply(invoke => invoke.Result),
Tags =
{
{ "Name", $"{prefix}-web-server" },
{ "ServerType", "WebServer" },
{ "Role", "WebServer" },
{ "Environment", environment },
{ "Owner", owner },
},
});
// EC2 Instance 2 - Database Server with "Role" tag
var databaseServer = new Aws.Ec2.Instance("database_server", new()
{
Ami = amazonLinux.Id,
InstanceType = Aws.Ec2.InstanceType.T3_Micro,
SubnetId = @default.Id,
VpcSecurityGroupIds = new[]
{
ec2Sg.Id,
},
IamInstanceProfile = ec2SsmProfile.Name,
UserData = Std.Base64encode.Invoke(new()
{
Input = @"#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install MySQL
yum install -y mysql-server
systemctl enable mysqld
systemctl start mysqld
",
}).Apply(invoke => invoke.Result),
Tags =
{
{ "Name", $"{prefix}-database-server" },
{ "Role", "Database" },
{ "Environment", environment },
{ "Owner", owner },
},
});
});
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 com.pulumi.aws.ec2.Instance;
import com.pulumi.aws.ec2.InstanceArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.Base64encodeArgs;
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) {
// SSM Association for Webbased Servers
var databaseAssociation = new Association("databaseAssociation", AssociationArgs.builder()
.name(systemUpdate.name())
.targets(AssociationTargetArgs.builder()
.key("tag:Role")
.values(
"WebServer",
"Database")
.build())
.parameters(Map.of("restartServices", "true"))
.scheduleExpression("cron(0 3 ? * SUN *)")
.build());
// EC2 Instance 1 - Web Server with "ServerType" tag
var webServer = new Instance("webServer", InstanceArgs.builder()
.ami(amazonLinux.id())
.instanceType("t3.micro")
.subnetId(default_.id())
.vpcSecurityGroupIds(ec2Sg.id())
.iamInstanceProfile(ec2SsmProfile.name())
.userData(StdFunctions.base64encode(Base64encodeArgs.builder()
.input("""
#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install Apache web server
yum install -y httpd
systemctl enable httpd
systemctl start httpd
echo \"<h1>Web Server - %s</h1>\" > /var/www/html/index.html
", prefix))
.build()).result())
.tags(Map.ofEntries(
Map.entry("Name", String.format("%s-web-server", prefix)),
Map.entry("ServerType", "WebServer"),
Map.entry("Role", "WebServer"),
Map.entry("Environment", environment),
Map.entry("Owner", owner)
))
.build());
// EC2 Instance 2 - Database Server with "Role" tag
var databaseServer = new Instance("databaseServer", InstanceArgs.builder()
.ami(amazonLinux.id())
.instanceType("t3.micro")
.subnetId(default_.id())
.vpcSecurityGroupIds(ec2Sg.id())
.iamInstanceProfile(ec2SsmProfile.name())
.userData(StdFunctions.base64encode(Base64encodeArgs.builder()
.input("""
#!/bin/bash
yum update -y
yum install -y amazon-ssm-agent
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Install MySQL
yum install -y mysql-server
systemctl enable mysqld
systemctl start mysqld
""")
.build()).result())
.tags(Map.ofEntries(
Map.entry("Name", String.format("%s-database-server", prefix)),
Map.entry("Role", "Database"),
Map.entry("Environment", environment),
Map.entry("Owner", owner)
))
.build());
}
}
resources:
# SSM Association for Webbased Servers
databaseAssociation:
type: aws:ssm:Association
name: database_association
properties:
name: ${systemUpdate.name}
targets:
- key: tag:Role
values:
- WebServer
- Database
parameters:
restartServices: 'true'
scheduleExpression: cron(0 3 ? * SUN *)
# EC2 Instance 1 - Web Server with "ServerType" tag
webServer:
type: aws:ec2:Instance
name: web_server
properties:
ami: ${amazonLinux.id}
instanceType: t3.micro
subnetId: ${default.id}
vpcSecurityGroupIds:
- ${ec2Sg.id}
iamInstanceProfile: ${ec2SsmProfile.name}
userData:
fn::invoke:
function: std:base64encode
arguments:
input: "#!/bin/bash\nyum update -y\nyum install -y amazon-ssm-agent\nsystemctl enable amazon-ssm-agent\nsystemctl start amazon-ssm-agent\n \n# Install Apache web server\nyum install -y httpd\nsystemctl enable httpd\nsystemctl start httpd\necho \\\"<h1>Web Server - ${prefix}</h1>\\\" > /var/www/html/index.html\n"
return: result
tags:
Name: ${prefix}-web-server
ServerType: WebServer
Role: WebServer
Environment: ${environment}
Owner: ${owner}
# EC2 Instance 2 - Database Server with "Role" tag
databaseServer:
type: aws:ec2:Instance
name: database_server
properties:
ami: ${amazonLinux.id}
instanceType: t3.micro
subnetId: ${default.id}
vpcSecurityGroupIds:
- ${ec2Sg.id}
iamInstanceProfile: ${ec2SsmProfile.name}
userData:
fn::invoke:
function: std:base64encode
arguments:
input: "#!/bin/bash\nyum update -y\nyum install -y amazon-ssm-agent\nsystemctl enable amazon-ssm-agent\nsystemctl start amazon-ssm-agent\n \n# Install MySQL\nyum install -y mysql-server\nsystemctl enable mysqld\nsystemctl start mysqld\n"
return: result
tags:
Name: ${prefix}-database-server
Role: Database
Environment: ${environment}
Owner: ${owner}
The values array can include multiple tag values. This association targets instances with Role=WebServer OR Role=Database, applying the same document to both groups. This reduces the number of associations you need to manage for similar maintenance tasks.
Beyond these examples
These snippets focus on specific association-level features: instance and tag-based targeting, scheduled execution with cron expressions, and shell script parameters and error controls. They’re intentionally minimal rather than full automation workflows.
The examples may 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
targets key to InstanceIds with values as ["*"]. This targets every managed instance in your account.key: "tag:YourTagName" in the targets block and specify the tag values in the values array. For example, key: "tag:Environment" with values: ["Development"] targets all instances with that tag.key to InstanceIds and provide an array of instance IDs in values, such as values: [instance1.id, instance2.id].Scheduling & Execution
scheduleExpression property with a cron or rate expression. For example, scheduleExpression: "cron(0 2 ? * SUN *)" runs the association at 2 AM every Sunday.applyOnlyAtCronInterval to true. By default, associations run immediately and then follow the schedule. Note that this parameter isn’t supported for rate expressions.Error Handling & Concurrency
maxConcurrency to specify either a number (e.g., "10") or a percentage (e.g., "10%"). This limits how many targets execute the association at the same time.maxErrors to a number or percentage. For example, maxErrors: "3" stops execution when the fourth error occurs, while "10%" stops at the error threshold based on your target count.Configuration & Requirements
name property is immutable, so changing it forces replacement of the entire association resource.Success status within the specified timeout, the create operation fails.