AWS Systems Manager (SSM) 入門教學:遠端管理 EC2 的瑞士刀
如果你曾經為了連進 EC2 而管理 SSH 金鑰、開放 Security Group 的 22 port,或在各台機器上散落著不同的設定檔與環境變數,AWS Systems Manager(簡稱 SSM) 就是為了解決這類痛點而生的。
SSM 是 AWS 提供的**營運管理(Operations)**服務,讓你能以統一、安全的方式管理 EC2、混合雲主機與邊緣裝置——不需要開 SSH port,也能遠端執行指令、管理設定、儲存機密參數。
這篇文章將帶你理解 SSM 的核心元件,並透過實務範例學會最常用的幾個功能。
什麼是 SSM?
Systems Manager 不是單一功能,而是一組營運工具集,常見用途包括:
| 功能 | 用途 | 典型場景 |
|---|---|---|
| Parameter Store | 集中儲存設定值與機密 | API Key、DB 連線字串、功能開關 |
| Session Manager | 瀏覽器或 CLI 遠端連線主機 | 取代 SSH,不需開放 22 port |
| Run Command | 對多台主機批次執行指令 | 安裝套件、重啟服務、收集 log |
| State Manager | 定期套用設定(類似 cron + Ansible) | 確保 agent 版本、定時清理暫存 |
| Patch Manager | 自動修補作業系統 | 定期安裝安全更新 |
對開發者來說,最常用的是前三項:Parameter Store、Session Manager、Run Command。
前置條件:讓主機「被 SSM 看見」
SSM 要能管理一台 EC2,必須滿足以下條件:
1. 安裝 SSM Agent
Amazon Linux 2 / Amazon Linux 2023、Ubuntu 16.04+ 等 AMI 通常已預裝 SSM Agent。其他系統需手動安裝,詳見 AWS 官方文件。
確認 agent 狀態(在 EC2 上執行):
sudo systemctl status amazon-ssm-agent
2. 綁定 IAM Instance Profile
EC2 需要一個 IAM Role,並附上 AmazonSSMManagedInstanceCore 管理政策(或等效的自訂 Policy),讓 SSM Agent 能向 AWS 註冊並通訊。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:UpdateInstanceInformation",
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel",
"ec2messages:AcknowledgeMessage",
"ec2messages:DeleteMessage",
"ec2messages:FailMessage",
"ec2messages:GetEndpoint",
"ec2messages:GetMessages",
"ec2messages:SendReply"
],
"Resource": "*"
}
]
}
實務上直接使用 AWS 託管政策
AmazonSSMManagedInstanceCore最省事。
3. 網路連線
EC2 必須能連到 SSM 的 endpoint,常見做法:
- 有 Public IP:透過 Internet Gateway 連外網
- Private Subnet:設定 NAT Gateway,或建立 VPC Interface Endpoint(
com.amazonaws.<region>.ssm、ssmmessages、ec2messages)
在 AWS Console → Systems Manager → Fleet Manager → Managed nodes 中,狀態顯示 Online 代表主機已就緒。
Parameter Store:集中管理設定與機密
Parameter Store 讓你把設定值存在 AWS,應用程式執行時再讀取,避免把密碼寫進 .env 或程式碼。
參數類型
| 類型 | 說明 | 適用 |
|---|---|---|
String | 明文儲存 | 非敏感設定(如 LOG_LEVEL=debug) |
StringList | 逗號分隔的清單 | 白名單 IP、允許的 domain |
SecureString | 使用 KMS 加密 | 密碼、API Token、私鑰 |
參數以階層路徑組織,例如 /myapp/prod/db/host、/myapp/prod/db/password。
CLI 建立參數
# 一般字串
aws ssm put-parameter \
--name "/myapp/prod/log-level" \
--value "info" \
--type String
# 加密機密(需指定 KMS Key,不指定則用預設 aws/ssm)
aws ssm put-parameter \
--name "/myapp/prod/db/password" \
--value "super-secret" \
--type SecureString
Python 讀取參數(boto3)
import boto3
from botocore.exceptions import ClientError
ssm = boto3.client("ssm", region_name="ap-northeast-1")
def get_parameter(name: str, *, decrypt: bool = False) -> str:
"""從 Parameter Store 讀取參數。"""
try:
response = ssm.get_parameter(Name=name, WithDecryption=decrypt)
return response["Parameter"]["Value"]
except ClientError as exc:
match exc.response["Error"]["Code"]:
case "ParameterNotFound":
raise ValueError(f"參數不存在: {name}") from exc
case _:
raise
# 讀取明文設定
log_level = get_parameter("/myapp/prod/log-level")
# 讀取加密機密
db_password = get_parameter("/myapp/prod/db/password", decrypt=True)
批次讀取(依路徑前綴)
def get_parameters_by_path(path: str, *, decrypt: bool = False) -> dict[str, str]:
"""一次讀取某路徑下所有參數,回傳 {參數名: 值}。"""
result: dict[str, str] = {}
paginator = ssm.get_paginator("get_parameters_by_path")
for page in paginator.paginate(Path=path, Recursive=True, WithDecryption=decrypt):
for param in page["Parameters"]:
result[param["Name"]] = param["Value"]
return result
config = get_parameters_by_path("/myapp/prod/", decrypt=True)
費用提示:Standard 參數免費(上限 10,000 個);Advanced 參數與較 大的值會收費。一般應用設定用 Standard 即可。
Session Manager:不用 SSH 也能連進主機
Session Manager 透過 SSM Agent 建立互動式 Shell 或 Port Forwarding,全程走 AWS API,不需要:
- 開放 Security Group 的 22 port
- 管理 SSH 金鑰
- 架設 Bastion Host
本機前置:安裝 Session Manager Plugin
# macOS(Homebrew)
brew install --cask session-manager-plugin
連線到 EC2
# 使用 Instance ID
aws ssm start-session --target i-0123456789abcdef0
# 指定特定使用者(預設為 ssm-user 或 ec2-user,視 AMI 而定)
aws ssm start-session \
--target i-0123456789abcdef0 \
--document-name AWS-StartInteractiveCommand \
--parameters command="sudo -i"
連線成功後,你會進入主機的 Shell,體驗類似 SSH,但流量是透過 AWS 加密通道傳輸。
Port Forwarding(常見於連 Private DB)
# 把本機 5432 轉發到 EC2 上的 RDS endpoint:5432
aws ssm start-session \
--target i-0123456789abcdef0 \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["mydb.xxxx.ap-northeast-1.rds.amazonaws.com"],"portNumber":["5432"],"localPortNumber":["5432"]}'
之後在本機執行 psql -h 127.0.0.1 -p 5432 即可連線,無需把資料庫暴露到公網。
操作者需要的 IAM 權限
{
"Effect": "Allow",
"Action": ["ssm:StartSession"],
"Resource": [
"arn:aws:ec2:ap-northeast-1:123456789012:instance/i-*",
"arn:aws:ssm:ap-northeast-1::document/AWS-StartSession"
]
}
建議搭配 IAM Condition 限制只能連特定 tag 的機器,並啟用 Session Manager 日誌(寫入 S3 或 CloudWatch),滿足稽核需求。
Run Command:批次遠端執行指令
需要在一台或多台主機上執行指令時,不必逐一 SSH 進去,改用 Run Command 即可。
CLI 範例
# 在所有帶有 Environment=prod tag 的機器上執行
aws ssm send-command \
--document-name "AWS-RunShellScript" \
--targets "Key=tag:Environment,Values=prod" \
--parameters 'commands=["sudo yum update -y","sudo systemctl restart myapp"]' \
--comment "Prod rolling restart"
查詢執行結果:
# 取得 Command ID 後查詢
aws ssm list-command-invocations \
--command-id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
--details
Python 範例
import time
def run_shell_on_instance(instance_id: str, commands: list[str]) -> str:
"""對單一 EC2 執行 Shell 指令,回傳 Command ID。"""
response = ssm.send_command(
InstanceIds=[instance_id],
DocumentName="AWS-RunShellScript",
Parameters={"commands": commands},
)
return response["Command"]["CommandId"]
def wait_for_command(command_id: str, instance_id: str, timeout: int = 120) -> str:
"""等待指令完成並回傳 stdout。"""
for _ in range(timeout):
result = ssm.get_command_invocation(CommandId=command_id, InstanceId=instance_id)
match result["Status"]:
case "Success":
return result.get("StandardOutputContent", "")
case "Failed" | "Cancelled" | "TimedOut":
raise RuntimeError(result.get("StandardErrorContent", result["Status"]))
case _:
time.sleep(1)
raise TimeoutError(f"指令逾時: {command_id}")
cmd_id = run_shell_on_instance("i-0123456789abcdef0", ["df -h", "free -m"])
output = wait_for_command(cmd_id, "i-0123456789abcdef0")
print(output)
AWS 也提供許多預建 Document(如 AWS-RunPowerShellScript、AWS-UpdateSSMAgent),可在 Console 的 Run Command 頁面瀏覽。
State Manager:讓設定持續生效
State Manager 類似「宣告式」的維運:你定義一台主機應該維持什麼狀態,SSM 會定期檢查並自動修正偏差。
常見用途:
- 確保 SSM Agent 保持最新版
- 定期清理
/tmp超過 7 天的檔案 - 確保 CloudWatch Agent 設定檔存在且服務運行中
在 Console → Systems Manager → State Manager 建立 Association,指定目標(Instance ID 或 Tag)與要套用的 Document 即可。
SSM 與 Secrets Manager 怎麼選?
兩者都能存機密,但定位不同:
| 特性 | Parameter Store | Secrets Manager |
|---|---|---|
| 主要用途 | 設定值 + 機密 | 專注於機密管理 |
| 自動輪替 | 不支援 | 支援(如 RDS 密碼) |
| 費用 | Standard 免費 | 按 secret 數量 + API 呼叫計費 |
| 適合場景 | 應用設定、功能開關、簡單機密 | 需要自動輪替的資料庫密碼 |
若只是存 API Key 或連線字串,Parameter Store 的 SecureString 通常就夠用;若需要與 RDS 整合自動換密碼,選 Secrets Manager。
最佳實踐
-
用 IAM Role,不要硬寫 Access Key EC2 / Lambda 上的程式透過 Instance Profile 或 Execution Role 讀取 Parameter Store,與 S3 串接 的作法相同。
-
參數路徑要有命名規範 建議
/專案名/環境/服務/參數名,例如/order-api/prod/payment/stripe-key,方便權限切割與批次讀取。 -
最小權限原則 應用程式 Role 只 允許
ssm:GetParameter/ssm:GetParameters在特定 ARN 路徑下;操作人員的 Session Manager 權限也應限制 tag 或 instance。 -
啟用 Session 日誌 將 Session Manager 輸出寫入 S3 或 CloudWatch Logs,方便事後稽核「誰在什麼時候連了哪台機器、做了什麼」。
-
Private 環境記得設 VPC Endpoint 沒有 NAT 的 Private Subnet 若缺少 SSM 相關 endpoint,agent 無法註冊,主機會一直顯示 Offline。
結語
AWS Systems Manager 把「連線、執行指令、管理設定」整合成一套以 IAM 權限 為核心的安全模型。對開發團隊而言,優先掌握 Parameter Store(取代散落的設定檔)、Session Manager(取代 SSH)與 Run Command(取代手動登入批次操作),就能大幅提升營運效率與安全性。
建議下一步:找一台測試用 EC2,綁上 AmazonSSMManagedInstanceCore,試著用 Session Manager 連線,並建立一個 SecureString 參數用 boto3 讀取——實際操作一次,比看十篇文件更有感。