AWS Secrets & Parameter 跨账号跨区域迁移指南
前言
在多账号架构或区域迁移场景中,Secrets Manager 和 Parameter Store 的数据迁移是常见需求。然而:
- Secrets Manager 原生复制功能仅支持同账号跨区域,不支持跨账号
- Parameter Store 不提供原生复制功能,跨账号共享需要 AWS RAM 且仅支持读取
本指南提供基于 AWS CLI 的一次性迁移方案,支持批量导出导入、并发执行、指数退避重试(避免 API 限流中断)以及 Tags 元数据同步。适用于以下场景:
- 多账号架构下的环境迁移(如从开发账号迁移到生产账号)
- 区域迁移或灾备部署
- 账号整合或拆分
方案概览
迁移脚本
1. Secrets Manager 迁移脚本
注意:此脚本同步 Secret 值和 Tags,不同步 Resource Policy(资源策略, 如允许某个用户/角色访问)。跨账号后源账号的 IAM 实体在目标账号可能不存在,直接复制策略没有意义,需在目标账号根据实际情况重新配置。
#!/bin/bash
# sync-secrets-manager.sh
# 跨账号跨区域同步 Secrets Manager(含 Tags 和重试)
set -e
# ============ 配置 ============
SOURCE_PROFILE="source-account" # 源账号 AWS profile
TARGET_PROFILE="target-account" # 目标账号 AWS profile
SOURCE_REGION="ap-northeast-1" # 东京
TARGET_REGION="eu-west-1" # 爱尔兰
MAX_RETRIES=3 # 最大重试次数
# ============ 带重试的 AWS 命令 ============
aws_retry() {
local i
for ((i=1; i<=MAX_RETRIES; i++)); do
if "$@" 2>/dev/null; then return 0; fi
[ $i -lt $MAX_RETRIES ] && sleep $((i * 2))
done
return 1
}
# ============ Secrets Manager 同步 ============
echo "=== 同步 Secrets Manager ==="
echo "源: $SOURCE_PROFILE ($SOURCE_REGION)"
echo "目标: $TARGET_PROFILE ($TARGET_REGION)"
echo "重试: $MAX_RETRIES 次"
echo ""
# 获取所有 secret 名称
aws secretsmanager list-secrets \
--region $SOURCE_REGION \
--profile $SOURCE_PROFILE \
--query 'SecretList[*].Name' \
--output json > /tmp/secret_names.json
count=$(jq length /tmp/secret_names.json)
echo "发现 $count 个 secrets"
# 逐个同步
jq -r '.[]' /tmp/secret_names.json | while read secret_name; do
echo "同步 secret: $secret_name"
# 获取 secret 值(带重试)
secret_value=$(aws_retry aws secretsmanager get-secret-value \
--secret-id "$secret_name" \
--region $SOURCE_REGION \
--profile $SOURCE_PROFILE \
--query 'SecretString' \
--output text) || continue
# 获取 secret 描述和 Tags
secret_info=$(aws_retry aws secretsmanager describe-secret \
--secret-id "$secret_name" \
--region $SOURCE_REGION \
--profile $SOURCE_PROFILE \
--output json)
description=$(echo "$secret_info" | jq -r '.Description // empty')
tags=$(echo "$secret_info" | jq -c '.Tags // []')
# 在目标账号创建或更新(带重试)
if aws secretsmanager describe-secret \
--secret-id "$secret_name" \
--region $TARGET_REGION \
--profile $TARGET_PROFILE &>/dev/null; then
aws_retry aws secretsmanager put-secret-value \
--secret-id "$secret_name" \
--secret-string "$secret_value" \
--region $TARGET_REGION \
--profile $TARGET_PROFILE
else
aws_retry aws secretsmanager create-secret \
--name "$secret_name" \
--secret-string "$secret_value" \
--description "${description:-Migrated from $SOURCE_REGION}" \
--region $TARGET_REGION \
--profile $TARGET_PROFILE
fi
# 同步 Tags(带重试)
if [ "$tags" != "[]" ] && [ -n "$tags" ]; then
aws_retry aws secretsmanager tag-resource \
--secret-id "$secret_name" \
--tags "$tags" \
--region $TARGET_REGION \
--profile $TARGET_PROFILE || true
fi
done
# 清理临时文件
rm -f /tmp/secret_names.json
echo ""
echo "=== Secrets Manager 同步完成 ==="
2. Parameter Store 迁移脚本
注意:此脚本同步参数值和 Tags。Parameter Store 不支持资源策略,访问控制通过 IAM Policy 管理。
#!/bin/bash
# sync-parameter-store.sh
# 跨账号跨区域同步 Parameter Store(含 Tags,支持并发和重试)
set -e
# ============ 配置 ============
SOURCE_PROFILE="source-account" # 源账号 AWS profile
TARGET_PROFILE="target-account" # 目标账号 AWS profile
SOURCE_REGION="ap-northeast-1" # 东京
TARGET_REGION="eu-west-1" # 爱尔兰
PARAM_PATH="/" # Parameter Store 路径,"/" 表示全部
PARALLEL=5 # 并发数
MAX_RETRIES=3 # 最大重试次数
# 导出变量供子进程使用
export SOURCE_PROFILE TARGET_PROFILE SOURCE_REGION TARGET_REGION MAX_RETRIES
# ============ 带重试的 AWS 命令 ============
aws_retry() {
local i
for ((i=1; i<=MAX_RETRIES; i++)); do
if "$@" 2>/dev/null; then return 0; fi
[ $i -lt $MAX_RETRIES ] && sleep $((i * 2))
done
return 1
}
export -f aws_retry
# ============ 同步单个参数 ============
sync_one_param() {
local idx="$1"
local param=$(jq -c ".[$idx]" /tmp/parameters.json)
local name=$(echo "$param" | jq -r '.Name')
local value=$(echo "$param" | jq -r '.Value')
local type=$(echo "$param" | jq -r '.Type')
echo "同步参数: $name"
# 创建或更新参数(带重试)
aws_retry aws ssm put-parameter \
--name "$name" \
--value "$value" \
--type "$type" \
--overwrite \
--region $TARGET_REGION \
--profile $TARGET_PROFILE || \
aws_retry aws ssm put-parameter \
--name "$name" \
--value "$value" \
--type "$type" \
--region $TARGET_REGION \
--profile $TARGET_PROFILE
# 获取并同步 Tags(带重试)
tags=$(aws_retry aws ssm list-tags-for-resource \
--resource-type "Parameter" \
--resource-id "$name" \
--region $SOURCE_REGION \
--profile $SOURCE_PROFILE \
--query 'TagList' \
--output json) || tags="[]"
if [ "$tags" != "[]" ] && [ -n "$tags" ]; then
aws_retry aws ssm add-tags-to-resource \
--resource-type "Parameter" \
--resource-id "$name" \
--tags "$tags" \
--region $TARGET_REGION \
--profile $TARGET_PROFILE || true
fi
}
export -f sync_one_param
# ============ Parameter Store 同步 ============
echo "=== 同步 Parameter Store ==="
echo "源: $SOURCE_PROFILE ($SOURCE_REGION)"
echo "目标: $TARGET_PROFILE ($TARGET_REGION)"
echo "并发数: $PARALLEL,重试: $MAX_RETRIES 次"
echo ""
# 导出参数
aws ssm get-parameters-by-path \
--path "$PARAM_PATH" \
--recursive \
--with-decryption \
--region $SOURCE_REGION \
--profile $SOURCE_PROFILE \
--query 'Parameters[*].{Name:Name,Value:Value,Type:Type}' \
--output json > /tmp/parameters.json
count=$(jq length /tmp/parameters.json)
echo "导出 $count 个参数"
# 并行导入(使用索引方式避免特殊字符问题)
seq 0 $((count-1)) | xargs -P $PARALLEL -I {} bash -c 'sync_one_param "$@"' _ {}
# 清理临时文件
rm -f /tmp/parameters.json
echo ""
echo "=== Parameter Store 同步完成 ==="
3. 使用方式
环境要求:
- Linux/macOS(或 WSL)
- AWS CLI v2
- jq
# 1. 配置 AWS profiles
aws configure --profile source-account # 源账号凭证
aws configure --profile target-account # 目标账号凭证
# 2. 修改脚本中的配置变量
# - SOURCE_PROFILE / TARGET_PROFILE
# - SOURCE_REGION / TARGET_REGION
# - PARAM_PATH(Parameter Store 路径过滤)
# - PARALLEL(并发数,默认 5)
# 3. 执行 Secrets Manager 迁移
chmod +x sync-secrets-manager.sh
./sync-secrets-manager.sh
# 4. 执行 Parameter Store 迁移
chmod +x sync-parameter-store.sh
./sync-parameter-store.sh
同步参考时间(跨区域 ap-northeast-1 → eu-west-1,PARALLEL=5):
| 数据量 | 耗时 |
|---|---|
| 10 Secrets | ~12 秒 |
| 50 Parameters | ~30 秒 |
IAM 权限要求
注意:请将以下 Policy 中的
ap-northeast-1、eu-west-1、SOURCE_ACCOUNT、TARGET_ACCOUNT替换为实际值。
1. 源账号权限
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParametersByPath",
"ssm:GetParameters",
"ssm:GetParameter",
"ssm:ListTagsForResource"
],
"Resource": "arn:aws:ssm:ap-northeast-1:SOURCE_ACCOUNT:parameter/*"
},
{
"Effect": "Allow",
"Action": [
"secretsmanager:ListSecrets",
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": "arn:aws:kms:ap-northeast-1:SOURCE_ACCOUNT:key/*"
}
]
}
2. 目标账号权限
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:PutParameter",
"ssm:AddTagsToResource"
],
"Resource": "arn:aws:ssm:eu-west-1:TARGET_ACCOUNT:parameter/*"
},
{
"Effect": "Allow",
"Action": [
"secretsmanager:CreateSecret",
"secretsmanager:PutSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:TagResource"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Resource": "arn:aws:kms:eu-west-1:TARGET_ACCOUNT:key/*"
}
]
}
安全注意事项
| 事项 | 说明 | 建议 |
|---|---|---|
| KMS 密钥 | SecureString/Secrets 在目标账号会用目标账号的 KMS 密钥重新加密 | 可指定 CMK |
| 敏感数据传输 | 脚本执行时敏感值会经过执行环境 | 在 EC2/CloudShell 执行 |
| 临时文件 | Parameter Store 脚本的 /tmp/parameters.json 含解密后的值 |
脚本已自动清理,执行环境应受限 |
| 审计日志 | 操作会记录在 CloudTrail | 确保两个账号都开启 |
| 网络安全 | API 调用走公网 | 可配置 VPC Endpoint |