name: aws-account-management description: 管理AWS账户、组织、IAM和账单。在设置AWS组织、管理IAM策略、控制成本或实施多账户策略时使用。触发于AWS组织、AWS IAM、AWS账单、Cost Explorer、SCPs、多账户、AWS SSO、Identity Center。
AWS账户管理
有效管理AWS账户、组织、IAM和账单。
AWS组织
组织结构
Root
├── 生产OU
│ ├── 生产账户A
│ └── 生产账户B
├── 开发OU
│ ├── 开发账户
│ └── 测试账户
├── 安全OU
│ ├── 安全账户
│ └── 日志归档账户
└── 沙盒OU
└── 沙盒账户
创建组织
# 创建组织(从管理账户)
aws organizations create-organization --feature-set ALL
# 创建组织单位
aws organizations create-organizational-unit \
--parent-id r-xxxx \
--name "Production"
# 创建成员账户
aws organizations create-account \
--email prod-aws@company.com \
--account-name "Production Account"
# 将账户移动到OU
aws organizations move-account \
--account-id 123456789012 \
--source-parent-id r-xxxx \
--destination-parent-id ou-xxxx-xxxxxxxx
服务控制策略(SCPs)
// 禁止离开组织
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyLeaveOrg",
"Effect": "Deny",
"Action": "organizations:LeaveOrganization",
"Resource": "*"
}
]
}
// 要求IMDSv2(实例元数据)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RequireIMDSv2",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotEquals": {
"ec2:MetadataHttpTokens": "required"
}
}
}
]
}
// 区域限制
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyNonApprovedRegions",
"Effect": "Deny",
"NotAction": [
"iam:*",
"organizations:*",
"support:*",
"budgets:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": ["us-east-1", "us-west-2", "eu-west-1"]
}
}
}
]
}
// 防止根用户访问
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRootUser",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:PrincipalArn": "arn:aws:iam::*:root"
}
}
}
]
}
附加SCP
# 创建SCP
aws organizations create-policy \
--name "DenyLeaveOrg" \
--type SERVICE_CONTROL_POLICY \
--content file://deny-leave-org.json
# 附加到OU
aws organizations attach-policy \
--policy-id p-xxxxxxxxxxxx \
--target-id ou-xxxx-xxxxxxxx
IAM身份中心(AWS SSO)
设置身份中心
# 启用身份中心
aws sso-admin create-instance
# 创建权限集
aws sso-admin create-permission-set \
--instance-arn arn:aws:sso:::instance/ssoins-xxxxxxxx \
--name "AdministratorAccess" \
--session-duration "PT8H"
# 附加托管策略
aws sso-admin attach-managed-policy-to-permission-set \
--instance-arn arn:aws:sso:::instance/ssoins-xxxxxxxx \
--permission-set-arn arn:aws:sso:::permissionSet/ssoins-xxxxxxxx/ps-xxxxxxxx \
--managed-policy-arn arn:aws:iam::aws:policy/AdministratorAccess
权限集
// 开发者权限集(内联策略)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DeveloperAccess",
"Effect": "Allow",
"Action": [
"ec2:*",
"s3:*",
"lambda:*",
"dynamodb:*",
"cloudwatch:*",
"logs:*"
],
"Resource": "*"
},
{
"Sid": "DenyBillingAndIAM",
"Effect": "Deny",
"Action": [
"iam:CreateUser",
"iam:DeleteUser",
"iam:CreateAccessKey",
"aws-portal:*",
"budgets:*"
],
"Resource": "*"
}
]
}
IAM最佳实践
IAM策略
// 最小权限策略示例
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3BucketAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "private"
}
}
},
{
"Sid": "AllowListBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket",
"Condition": {
"StringLike": {
"s3:prefix": ["${aws:username}/*"]
}
}
}
]
}
// 跨账户角色信任策略
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "unique-external-id"
},
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
]
}
IAM服务角色
// Lambda执行角色
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
// EC2实例配置文件
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
IAM安全工具
# 生成凭证报告
aws iam generate-credential-report
aws iam get-credential-report --output text --query Content | base64 -d
# 列出未使用的访问密钥(上次使用 > 90天)
aws iam list-users --query 'Users[*].UserName' --output text | \
xargs -I {} aws iam list-access-keys --user-name {} \
--query 'AccessKeyMetadata[?Status==`Active`]'
# 获取访问密钥上次使用时间
aws iam get-access-key-last-used --access-key-id AKIAXXXXXXXX
# IAM访问分析器
aws accessanalyzer create-analyzer \
--analyzer-name my-analyzer \
--type ACCOUNT
成本管理
AWS预算
# 创建预算
aws budgets create-budget \
--account-id 123456789012 \
--budget '{
"BudgetName": "Monthly-Budget",
"BudgetLimit": {
"Amount": "1000",
"Unit": "USD"
},
"BudgetType": "COST",
"TimeUnit": "MONTHLY"
}' \
--notifications-with-subscribers '[
{
"Notification": {
"NotificationType": "ACTUAL",
"ComparisonOperator": "GREATER_THAN",
"Threshold": 80
},
"Subscribers": [
{
"SubscriptionType": "EMAIL",
"Address": "alerts@company.com"
}
]
}
]'
Cost Explorer API
import boto3
from datetime import datetime, timedelta
client = boto3.client('ce')
# 获取成本和用量
response = client.get_cost_and_usage(
TimePeriod={
'Start': (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d'),
'End': datetime.now().strftime('%Y-%m-%d')
},
Granularity='MONTHLY',
Metrics=['UnblendedCost'],
GroupBy=[
{'Type': 'DIMENSION', 'Key': 'SERVICE'},
{'Type': 'DIMENSION', 'Key': 'LINKED_ACCOUNT'}
]
)
# 获取成本预测
forecast = client.get_cost_forecast(
TimePeriod={
'Start': datetime.now().strftime('%Y-%m-%d'),
'End': (datetime.now() + timedelta(days=30)).strftime('%Y-%m-%d')
},
Metric='UNBLENDED_COST',
Granularity='MONTHLY'
)
print(f"预测成本: ${forecast['Total']['Amount']}")
成本分配标签
# 激活成本分配标签
aws ce update-cost-allocation-tags-status \
--cost-allocation-tags-status '[
{"TagKey": "Environment", "Status": "Active"},
{"TagKey": "Project", "Status": "Active"},
{"TagKey": "CostCenter", "Status": "Active"}
]'
# 一致地标记资源
aws ec2 create-tags \
--resources i-1234567890abcdef0 \
--tags Key=Environment,Value=Production \
Key=Project,Value=WebApp \
Key=CostCenter,Value=Engineering
Savings Plans与预留实例
# 获取Savings Plans推荐
aws savingsplans describe-savings-plans-offering-rates \
--savings-plan-offering-ids xxxxxxxxx
# 获取预留实例推荐
aws ce get-reservation-purchase-recommendation \
--service "Amazon Elastic Compute Cloud - Compute" \
--lookback-period-in-days THIRTY_DAYS \
--term-in-years ONE_YEAR \
--payment-option NO_UPFRONT
CloudTrail与日志记录
组织追踪
# 创建组织追踪
aws cloudtrail create-trail \
--name organization-trail \
--s3-bucket-name my-cloudtrail-bucket \
--is-organization-trail \
--is-multi-region-trail \
--enable-log-file-validation \
--kms-key-id alias/cloudtrail-key
# 开始日志记录
aws cloudtrail start-logging --name organization-trail
CloudTrail事件选择器
# 日志管理事件和S3数据事件
aws cloudtrail put-event-selectors \
--trail-name organization-trail \
--event-selectors '[
{
"ReadWriteType": "All",
"IncludeManagementEvents": true,
"DataResources": [
{
"Type": "AWS::S3::Object",
"Values": ["arn:aws:s3:::sensitive-bucket/"]
}
]
}
]'
Config与合规
AWS Config规则
# 启用Config
aws configservice put-configuration-recorder \
--configuration-recorder name=default,roleARN=arn:aws:iam::123456789012:role/config-role
# 部署托管规则
aws configservice put-config-rule \
--config-rule '{
"ConfigRuleName": "s3-bucket-public-read-prohibited",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"
}
}'
# 组织Config规则
aws configservice put-organization-config-rule \
--organization-config-rule-name "org-s3-bucket-public-read-prohibited" \
--organization-managed-rule-metadata '{
"RuleIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"
}'
合规包
# conformance-pack.yaml
Parameters:
S3BucketName:
Type: String
Resources:
S3BucketPublicReadProhibited:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: s3-bucket-public-read-prohibited
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_PUBLIC_READ_PROHIBITED
IAMRootAccessKeyCheck:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: iam-root-access-key-check
Source:
Owner: AWS
SourceIdentifier: IAM_ROOT_ACCESS_KEY_CHECK
MFAEnabledForIAMConsoleAccess:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: mfa-enabled-for-iam-console-access
Source:
Owner: AWS
SourceIdentifier: MFA_ENABLED_FOR_IAM_CONSOLE_ACCESS
Terraform多账户
# providers.tf
provider "aws" {
alias = "management"
region = "us-east-1"
}
provider "aws" {
alias = "production"
region = "us-east-1"
assume_role {
role_arn = "arn:aws:iam::${var.prod_account_id}:role/TerraformRole"
}
}
provider "aws" {
alias = "development"
region = "us-east-1"
assume_role {
role_arn = "arn:aws:iam::${var.dev_account_id}:role/TerraformRole"
}
}
# 在特定账户中创建资源
resource "aws_s3_bucket" "prod_bucket" {
provider = aws.production
bucket = "my-prod-bucket"
}
resource "aws_s3_bucket" "dev_bucket" {
provider = aws.development
bucket = "my-dev-bucket"
}
账户工厂(Control Tower模式)
# modules/account/main.tf
resource "aws_organizations_account" "account" {
name = var.account_name
email = var.account_email
parent_id = var.organizational_unit_id
role_name = "OrganizationAccountAccessRole"
tags = {
Environment = var.environment
ManagedBy = "Terraform"
}
}
resource "aws_iam_role" "terraform_role" {
provider = aws.new_account
name = "TerraformRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${var.management_account_id}:root"
}
Action = "sts:AssumeRole"
}
]
})
}
安全最佳实践清单
## 账户安全
- [ ] 根账户启用MFA
- [ ] 删除根账户访问密钥
- [ ] 根账户电子邮件是分发列表
- [ ] 配置强密码策略
- [ ] 在所有区域启用CloudTrail
- [ ] 启用GuardDuty
- [ ] 启用Security Hub
- [ ] 启用Config并配置规则
## 组织安全
- [ ] SCP限制危险操作
- [ ] SCP强制执行区域限制
- [ ] SCP要求加密
- [ ] 日志归档账户隔离
- [ ] 安全账户隔离
- [ ] 跨账户访问使用角色(非用户)
## IAM安全
- [ ] 无长期访问密钥
- [ ] 启用IAM访问分析器
- [ ] 轮换/删除未使用凭证
- [ ] 委托管理员使用权限边界
- [ ] 尽可能使用服务链接角色
## 成本管理
- [ ] 配置预算并设置警报
- [ ] 激活成本分配标签
- [ ] 评估Savings Plans
- [ ] 清理未使用资源
- [ ] 审查优化建议
资源
- 组织文档: https://docs.aws.amazon.com/organizations/
- IAM最佳实践: https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html
- 身份中心: https://docs.aws.amazon.com/singlesignon/
- 成本管理: https://docs.aws.amazon.com/cost-management/
- Control Tower: https://docs.aws.amazon.com/controltower/
- Security Hub: https://docs.aws.amazon.com/securityhub/