
August 18, 2026
10 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Managing dozens of servers via individual SSH connections is a security risk and an operational bottleneck that does not scale. AWS Systems Manager: Automate Fleet Operations solves this by providing a unified interface to configure, patch, and secure your entire infrastructure without opening inbound ports. For teams building scalable and efficient systems, adopting SSM is often the turning point between fragile manual administration and reliable, auditable automation.
How does AWS Systems Manager automate fleet operations securely?
At its core, AWS Systems Manager (SSM) inverts the traditional connectivity model. Instead of you reaching into the server via SSH on port 22, the SSM Agent installed on each instance maintains a persistent, encrypted outbound connection to the AWS service endpoint. This architecture means your instances can reside entirely in private subnets with no public IP addresses and no inbound security group rules required for management.
This shift has profound implications for security posture. In my experience working on production infrastructure for legal-tech portals where data sensitivity is paramount, removing public SSH access reduces the attack surface dramatically. You no longer worry about brute-force attacks on port 22 or managing rotating SSH key pairs across a growing team. Access control shifts entirely to AWS IAM policies, which are far more granular and auditable than Linux user accounts.
The SSM Agent comes pre-installed on Amazon Linux 2023, Ubuntu 22.04/24.04 LTS, and Windows Server 2019+ AMIs. For older instances or hybrid environments, installation is a single command. The agent requires only outbound HTTPS (port 443) access to specific regional SSM endpoints, making it compatible with strict egress-filtered networks. Once registered, the instance appears in the SSM Fleet Manager console within minutes, ready for automated operations.
What are the essential IAM permissions for SSM automation?
Before running any commands, you must configure two distinct IAM roles correctly. Misconfiguring these is the most common reason SSM fails in new deployments. The first role attaches to the EC2 instance itself; the second governs what human operators or CI/CD pipelines can do.
Instance Profile Configuration
Every managed node needs an instance profile with the AmazonSSMManagedInstanceCore managed policy. This grants the minimum permissions for the agent to communicate with the service, send heartbeats, and fetch commands. Avoid attaching administrator policies to instances; this violates least-privilege principles and creates lateral movement risks if an instance is compromised.
<!-- Minimal trust policy for EC2 instance role -->
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
<!-- Attach AmazonSSMManagedInstanceCore + CloudWatchAgentServerPolicy --> If your instances run in private subnets without NAT gateways, add VPC endpoints for ssm, ssmmessages, and ec2messages. Without these, the agent cannot reach the service even with correct IAM roles. I have debugged this exact issue on client projects where instances appeared unmanaged despite perfect IAM configuration—the missing VPC endpoint was always the culprit.
Operator Permissions
Human users and automation scripts need separate permissions. The AmazonSSMFullAccess managed policy works for initial setup but is too broad for production. Create a custom policy scoped to specific resource tags or instance IDs:
ssm:SendCommandrestricted to approved SSM documents onlyssm:StartSessionlimited to tagged production instancesssm:GetCommandInvocationfor viewing execution results- Explicit deny on
ssm:UpdateDocumentunless part of platform engineering team
This tag-based scoping prevents junior developers from accidentally running destructive commands against production databases while still allowing them to debug staging environments freely.
How do you execute remote commands at scale with Run Command?
Run Command is the workhorse of AWS Systems Manager: Automate Fleet Operations. It executes shell scripts, PowerShell commands, or Ansible playbooks across hundreds of instances simultaneously without writing custom orchestration code. Unlike ad-hoc SSH loops, every execution is logged to CloudTrail and output captured in S3 or CloudWatch Logs automatically.
Targeting Instances Intelligently
Never target instances by ID in production automation. Use resource tags instead. Tags survive instance replacements during auto-scaling events and make your commands portable across environments. Combine multiple tag filters for precision:
aws ssm send-command \
--document-name "AWS-RunShellScript" \
--targets "Key=tag:Environment,Values=production" "Key=tag:Role,Values=webserver" \
--parameters 'commands=["sudo systemctl restart php8.4-fpm","sudo systemctl status php8.4-fpm"]' \
--max-concurrency "20%" \
--max-errors "5%" \
--output-s3-bucket-name "my-ssm-logs" \
--region ap-south-1 The --max-concurrency and --max-errors parameters are critical safety valves. Setting concurrency to 20% ensures you never restart all web servers simultaneously during peak traffic. The error threshold stops execution if more than 5% of targets fail, preventing cascading failures from bad scripts. I treat these as mandatory, not optional—every production Run Command includes both.
Custom SSM Documents for Repeatable Tasks
While AWS-RunShellScript works for one-off debugging, create custom SSM documents for recurring operational tasks. Documents version-control your automation, accept typed parameters, and enforce validation before execution:
{
"schemaVersion": "2.2",
"description": "Clear Laravel caches and rebuild config",
"parameters": {
"AppPath": {
"type": "String",
"default": "/var/www/html/current",
"description": "Laravel release path"
}
},
"mainSteps": [
{
"action": "aws:runShellScript",
"name": "ClearCaches",
"inputs": {
"runCommand": [
"cd {{ AppPath }}",
"sudo -u www-data php artisan config:clear",
"sudo -u www-data php artisan cache:clear",
"sudo -u www-data php artisan view:clear",
"sudo -u www-data php artisan config:cache"
]
}
}
]
} Store these documents in Git alongside your application code. Deploy them via CI/CD so your automation evolves with your application. This pattern keeps operational knowledge codified rather than trapped in individual engineers' heads or bash history files.
How does Patch Manager maintain compliance across mixed fleets?
Patch Manager transforms OS patching from a monthly fire drill into a predictable, automated workflow. It supports Amazon Linux, Ubuntu, Debian, RHEL, CentOS, and Windows Server from a single interface. More importantly, it separates patch approval from patch deployment—a distinction many teams miss until they accidentally push a breaking kernel update to production.
| Capability | Manual Patching | SSM Patch Manager |
|---|---|---|
| Coverage Visibility | Spreadsheet tracking, stale data | Real-time compliance dashboard per instance |
| Approval Workflow | Ad-hoc testing, tribal knowledge | Patch baselines with explicit allow/deny lists |
| Maintenance Windows | Coordinated manually via chat/email | Scheduled cron-like windows with timezone support |
| Rollback Capability | Snapshot-dependent, slow | Pre-patch snapshots + documented rollback runbooks |
| Audit Evidence | Screenshots, log fragments | Automated compliance reports exportable to PDF/CSV |
| Hybrid Support | Separate tooling per platform | Unified baseline across cloud and on-premises |
Create separate patch baselines for staging and production. Staging should auto-approve patches after 3 days of release; production might require 14 days plus manual approval for critical CVEs. Maintenance windows enforce when patches install regardless of approval state—this prevents 3 AM surprise reboots. For Nepal-based clients operating on NPT (UTC+5:45), always specify timezone explicitly in maintenance window schedules to avoid off-by-one-hour errors during BST transitions.
Patch Manager integrates natively with AWS Security Hub and Inspector. Compliance drift triggers automated alerts before auditors notice gaps. For legal-tech platforms handling sensitive case data, this continuous compliance evidence generation saves dozens of hours during annual security reviews.
When should you use Session Manager instead of SSH?
Session Manager provides interactive shell access without SSH keys, bastion hosts, or open ports. It is not a replacement for all SSH use cases—it is superior for debugging, inferior for file transfers and tunneling. Understanding when to use each prevents friction.
Enable Session Manager logging to S3 or CloudWatch Logs immediately. Every keystroke and command output gets recorded. This audit trail proved invaluable during a security review for a financial services client who needed to demonstrate exactly who accessed production servers and what they did. Traditional SSH leaves no such centralized record without additional tooling like Teleport or BastionZero.
For local development workflows requiring port forwarding (database GUIs, admin panels), SSM supports SSH-style tunneling without exposing ports publicly:
aws ssm start-session \
--target i-0abc123def456 \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["localhost"],"portNumber":["3306"], "localPortNumber":["3306"]}' This forwards your local port 3306 directly to the RDS instance accessible only from the EC2 host. No VPN, no jump box, no security group changes. Developers get database access for debugging while the infrastructure team maintains zero public exposure. If you are exploring cloud hosting versus shared hosting, this capability alone often justifies the migration cost for teams needing secure developer access.
How do you integrate SSM with existing CI/CD and monitoring stacks?
SSM does not operate in isolation. Its real power emerges when integrated with your deployment pipelines and observability stack. Treat SSM documents as infrastructure-as-code artifacts stored in version control, deployed through the same CI/CD system as your application.
In GitLab CI pipelines I maintain for multiple sister sites sharing infrastructure, post-deployment jobs invoke SSM Run Command to clear caches, reload PHP-FPM, and verify health checks. The pipeline waits for command completion and fails the job if any target returns non-zero exit codes. This catches deployment issues before marking the release successful. Combined with DevOps automation best practices, this creates end-to-end visibility from commit to production verification.
Connect SSM OpsCenter to CloudWatch Alarms for automated remediation. When disk usage exceeds 85%, trigger an SSM Automation document that cleans temp files, rotates logs, and notifies Slack. When certificate expiry approaches, auto-renew via Certbot and reload Nginx. These self-healing patterns reduce pager fatigue and let engineers focus on feature work instead of repetitive firefighting.
Export SSM inventory data to Athena for long-term trend analysis. Track package versions, agent status, and compliance scores over months. This historical data informs capacity planning and identifies configuration drift before it causes incidents. For agencies managing dozens of client environments, this centralized visibility replaces fragmented spreadsheet tracking and missed renewal dates.
Practical Next Steps for AWS Systems Manager Adoption
Start with Session Manager to eliminate SSH keys across your fleet this week. Then implement Run Command for your three most frequent manual tasks. Finally, establish Patch Manager baselines aligned with your compliance requirements. Each step delivers immediate value while building toward comprehensive AWS Systems Manager: Automate Fleet Operations coverage. If your team needs hands-on implementation support or infrastructure assessment, reach out to discuss your specific environment.

