Skip to main content

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 StoreSession ManagerRun 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 Endpointcom.amazonaws.<region>.ssmssmmessagesec2messages

在 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 建立互動式 ShellPort 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-RunPowerShellScriptAWS-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 StoreSecrets Manager
主要用途設定值 + 機密專注於機密管理
自動輪替不支援支援(如 RDS 密碼)
費用Standard 免費按 secret 數量 + API 呼叫計費
適合場景應用設定、功能開關、簡單機密需要自動輪替的資料庫密碼

若只是存 API Key 或連線字串,Parameter Store 的 SecureString 通常就夠用;若需要與 RDS 整合自動換密碼,選 Secrets Manager。

最佳實踐

  1. 用 IAM Role,不要硬寫 Access Key EC2 / Lambda 上的程式透過 Instance Profile 或 Execution Role 讀取 Parameter Store,與 S3 串接 的作法相同。

  2. 參數路徑要有命名規範 建議 /專案名/環境/服務/參數名,例如 /order-api/prod/payment/stripe-key,方便權限切割與批次讀取。

  3. 最小權限原則 應用程式 Role 只允許 ssm:GetParameter / ssm:GetParameters 在特定 ARN 路徑下;操作人員的 Session Manager 權限也應限制 tag 或 instance。

  4. 啟用 Session 日誌 將 Session Manager 輸出寫入 S3 或 CloudWatch Logs,方便事後稽核「誰在什麼時候連了哪台機器、做了什麼」。

  5. 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 讀取——實際操作一次,比看十篇文件更有感。