简介这份资源围绕 AWS Landing Zone 的自动化部署与测试展开面向已具备一定云基础、需要落地多账户治理体系的架构师与运维开发人员帮助解决组织单元划分、核心账户创建、安全基线配置及策略校验等实际问题。包内共 69 个文件以 json 策略模板、md 说明文档、yml CloudFormation 配置为主辅以 rb 校验脚本与少量 pdf、docx 参考资料压缩包约 1.85MB按测试环境搭建、组织与 OU 管理、账户与联合访问、安全基线、示例工作负载等模块分层组织。内容涵盖 CloudTrail、Config、VPC Flow Logs、S3 桶策略等安全配置模板以及多层级 VPC 示例与跨账户策略样例可辅助读者理解着陆区的整体结构与配置逻辑。目前已有 310 人学习适合作为搭建与验证 Landing Zone 环境的参考材料。1. 从一份“能跑起来”的 Landing Zone 说起很多团队第一次搭 AWS 多账号环境都是先在控制台里手工点出来的开账号、建 OU、挂 SCP、配日志桶、拉网络对等。环境确实能跑但三个月后没人说得清某个 SCP 为什么挂在那个 OU 上新加一个业务账号要复制粘贴十几步改错一条策略还得靠回忆回滚。这套 landing-zone 示例要解决的正是这个问题——把 Landing Zone 的搭建、策略下发和验证过程写成可重复执行的 Python 代码与配套文档让多账号治理从“手工活”变成“可测试的工程”。它适合正在做云平台方向毕设或课程设计的学生也适合需要交付一套多账号基线方案、又不想从零写脚本的从业者。下面按需求、设计、实现、避坑、验收的顺序拆开讲中间给到的结构和参数可以直接照着改。2. 需求与边界Landing Zone 到底要交付什么2.1 先把“多账号治理”拆成四类硬需求Landing Zone 这个词容易被讲得很虚落到交付物上我一般把它拆成四件事。第一是账号结构哪些账号归到哪个 OU生产、非生产、沙箱、共享服务怎么分层这决定了后面所有策略的作用范围。第二是护栏策略SCP 限制哪些高危操作比如关闭 CloudTrail、删除日志桶、开放安全组到 0.0.0.0/0以及哪些区域允许使用。第三是集中化基础设施日志归档账号里的 CloudTrail 组织级跟踪、Config 聚合、集中告警这些是审计和排障的黑匣子。第四是网络与身份基线跨账号的 VPC 互联方式、IAM 角色信任关系、权限边界。这四类需求如果不在方案开头写清楚后面代码写得再漂亮也只是散装脚本。从毕设或课设的角度看这四类需求正好对应四个可验收的模块评分点清晰结构对不对、策略生效不生效、日志收得上不上、跨账号能不能通。从业者交付时也一样甲方问的往往就是这四件事而不是你用了多少个 Python 文件。2.2 技术选型为什么是 Python 加 IaC而不是纯控制台选型上有一个反直觉的点Landing Zone 的“自动化”不等于“全部用 IaC 声明”。账号创建、OU 移动这类组织级操作用基础设施即代码工具做往往比控制台还别扭因为组织单元的状态变更涉及异步传播。这套示例的思路是分层组织结构和账号生命周期用 Python 调 SDK 封装成幂等脚本策略和资源配置用声明式模板管理测试层单独用 Python 写断言。这样做的理由是Python 的 boto3 对 Organizations、SCP、CloudTrail 这些服务的覆盖足够全写幂等逻辑先查后建、存在即跳过比硬套声明式工具直观而且测试代码和实现代码同语言学生改起来门槛低。提示选型时先确认目标区域和服务配额Organizations 的账号创建有速率限制脚本里必须带退避重试否则批量建号必翻车。2.3 方案边界哪些不做避免范围失控一份能交付的方案必须写清“不做什么”。这套示例不覆盖计费与成本分摊的精细账单处理不覆盖第三方安全产品的集成也不做跨云的统一治理。网络部分只给到 VPC 对等和 Transit Gateway 的挂载示例不展开完整的 SD-WAN 设计。把这些边界写进文档开头答辩时被问到“为什么没做成本优化”就能直接指过去而不是临场编。范围收敛还有一个好处代码量可控测试能覆盖全验收时每条需求都能指到具体文件。3. 总体架构设计分层结构与模块职责划分3.1 四层结构组织层、策略层、资源层、验证层整体架构按四层切分每层只依赖下一层的输出不跨层调用。组织层负责账号和 OU 的创建与归属输出一份账号清单JSON记录账号 ID、邮箱、所属 OU、用途标签。策略层读取这份清单按 OU 映射下发 SCP 和权限边界同时把策略内容版本化存放在仓库里。资源层在指定账号内创建基线资源日志桶、组织级 CloudTrail、Config 记录器、默认安全组规则。验证层不参与创建只做只读断言输入是前三层的实际状态输出是测试报告。这样分层的好处是任何一层出问题都能单独重跑验证层还能在别人手工改坏环境后当回归测试用。landing-zone/ ├── org/ # 组织层账号与 OU 管理 ├── policies/ # 策略层SCP 与权限边界模板 ├── baseline/ # 资源层日志、Config、网络基线 ├── tests/ # 验证层只读断言与报告 └── docs/ # 设计说明与运行手册目录结构本身就是交付物的一部分评审时先看目录就知道方案有没有分层。我一般要求每个目录下有一个 README 说明输入输出避免半年后接手的人靠读代码猜意图。3.2 账号清单作为单一事实来源整个方案里最重要的数据结构是账号清单。它是一份 JSON由组织层脚本生成或维护策略层和验证层都只读它。字段设计上至少包含account_id、account_name、email、ou_path、environment、owner、created_at。ou_path用斜杠分隔的路径表示层级比如Root/Workloads/Prod这样策略层可以用前缀匹配决定挂哪条 SCP。environment用于测试断言比如生产账号必须挂禁止删除日志桶的策略。把清单当单一事实来源能避免“代码里写死账号 ID、文档里写另一套”的经典翻车。{ accounts: [ { account_id: 111122223333, account_name: shared-log-archive, email: aws-logexample.internal, ou_path: Root/Infrastructure/Security, environment: shared, owner: platform-team }, { account_id: 444455556666, account_name: workload-prod-a, email: aws-prod-aexample.internal, ou_path: Root/Workloads/Prod, environment: prod, owner: app-team-a } ] }这份清单的维护方式通常是新账号走一次申请流程人工补充邮箱和归属脚本负责创建和校验。注意邮箱必须全局唯一AWS 对账号邮箱有唯一性约束重复邮箱会导致创建失败这是批量建号时最常见的报错来源。3.3 策略映射表OU 到 SCP 的对应关系策略层不把 SCP 直接写死在脚本里而是用一张映射表描述“哪个 OU 路径挂哪些策略”。映射表同样是 JSON键是 OU 路径前缀值是策略文件列表。脚本遍历账号清单对每个账号取最长前缀匹配得到应挂的策略集合再和实际挂载情况做差集只做增删。这种“声明期望状态、计算差异、执行变更”的模式是幂等脚本的核心重跑不会重复挂载也不会误删手工加的例外策略例外策略单独标记为manual脚本跳过。# policies/apply_scp.py import json from botocore.exceptions import ClientError def desired_policies(ou_path, mapping): 按最长前缀匹配返回该 OU 应挂的策略名集合 best, result , set() for prefix, names in mapping.items(): if ou_path.startswith(prefix) and len(prefix) len(best): best, result prefix, set(names) return result def sync_scp(org_client, target_id, desired, manual()): 对比实际挂载与期望只做增删manual 中的策略不动 attached { p[Name] for p in org_client.list_policies_for_target( TargetIdtarget_id, FilterSERVICE_CONTROL_POLICY )[Policies] } to_attach desired - attached to_detach attached - desired - set(manual) for name in to_attach: policy_id find_policy_id(org_client, name) org_client.attach_policy(PolicyIdpolicy_id, TargetIdtarget_id) for name in to_detach: policy_id find_policy_id(org_client, name) org_client.detach_policy(PolicyIdpolicy_id, TargetIdtarget_id) return {attached: sorted(to_attach), detached: sorted(to_detach)}这段代码的关键在sync_scp的差集逻辑to_attach和to_detach都基于实际状态计算所以脚本可以反复执行。manual参数是给人工例外留的口子比如某个 OU 临时需要放开一条限制做压测标记后脚本不会把它摘掉。find_policy_id需要按名称查策略 ID实际实现里要处理分页Organizations 的列表接口默认只返回有限条数账号多了必须翻页否则会漏策略。参数上TargetId既可以是 OU 也可以是账号映射表按 OU 配个别账号的特殊策略再单独处理。4. 关键模块实现从建号到基线资源落地4.1 账号创建与 OU 归属的幂等脚本账号创建是整套流程里最容易出问题的一步因为它是异步的。create_account调用返回的是CreateAccountStatus状态从IN_PROGRESS到SUCCEEDED需要几十秒到几分钟。脚本必须轮询状态成功后再把账号移到目标 OU。幂等的做法是先按邮箱查已有账号存在就跳过创建只做 OU 归属校正。下面这段是核心逻辑省略了日志和异常包装。# org/create_accounts.py import time def ensure_account(org_client, spec, poll_interval10, timeout600): 按邮箱查重不存在则创建并等待完成返回账号 ID existing org_client.list_accounts()[Accounts] for acc in existing: if acc[Email].lower() spec[email].lower(): return acc[Id] req_id org_client.create_account( Emailspec[email], AccountNamespec[account_name], )[CreateAccountStatus][Id] waited 0 while waited timeout: status org_client.describe_create_account_status( CreateAccountStatusIdreq_id )[CreateAccountStatus] if status[State] SUCCEEDED: return status[AccountId] if status[State] FAILED: raise RuntimeError(f创建失败: {status.get(FailureReason)}) time.sleep(poll_interval) waited poll_interval raise TimeoutError(f账号创建超时: {spec[email]})poll_interval默认 10 秒timeout给 600 秒批量建号时这两个值要按配额调账号创建有并发上限串行执行最稳。list_accounts同样要处理分页示例里为了可读性省略了NextToken循环实际代码必须补上。创建成功后调用move_account把账号移到ou_path对应的 OU移动前要先按路径逐级查 OU ID路径不存在就报错而不是自动创建避免拼错路径悄悄建出错误层级。4.2 组织级 CloudTrail 与日志归档的配置日志基线是审计的根配置顺序不能乱先在日志归档账号建桶并设好桶策略再在管理账号创建组织级跟踪指向该桶。桶策略要允许 Organizations 的跟踪写入同时拒绝非 TLS 访问和删除操作。下面这段是桶策略的关键部分aws:SourceAccount和aws:SourceArn条件用来防止 confused deputy 问题。{ Version: 2012-10-17, Statement: [ { Sid: AllowOrgTrailWrite, Effect: Allow, Principal: {Service: cloudtrail.amazonaws.com}, Action: s3:PutObject, Resource: arn:aws:s3:::org-trail-logs/AWSLogs/*, Condition: { StringEquals: {aws:SourceAccount: 999900001111}, ArnLike: {aws:SourceArn: arn:aws:cloudtrail:us-east-1:999900001111:trail/org-trail} } }, { Sid: DenyInsecureTransport, Effect: Deny, Principal: *, Action: s3:*, Resource: [arn:aws:s3:::org-trail-logs, arn:aws:s3:::org-trail-logs/*], Condition: {Bool: {aws:SecureTransport: false}} } ] }桶策略里SourceAccount填管理账号 IDSourceArn填跟踪的 ARN这两个条件缺一个都可能被安全评审打回。桶还要开版本控制和对象锁如果合规要求生命周期规则把日志转到低频存储。跟踪创建时勾选“应用于所有账号”这样新账号自动纳入不用逐个配。注意日志桶所在区域和跟踪区域要一致跨区写入会失败这是配置时最常见的报错。4.3 验证层把“应该生效”写成断言验证层是这套方案区别于普通脚本合集的地方。它不创建任何资源只读实际状态并断言。测试用 pytest 组织每个测试对应一条需求比如“生产 OU 下所有账号必须挂禁止删除日志桶的 SCP”“组织级跟踪必须处于 logging 状态”“日志桶必须拒绝非 TLS 访问”。断言失败时输出账号 ID 和实际值方便定位。下面是一个测试样例。# tests/test_guardrails.py import json import pytest pytest.fixture(scopesession) def accounts(): with open(org/accounts.json) as f: return json.load(f)[accounts] def test_prod_has_protect_logs_scp(org_client, accounts): 生产账号必须挂载 ProtectLogs 策略 for acc in accounts: if acc[environment] ! prod: continue names { p[Name] for p in org_client.list_policies_for_target( TargetIdacc[account_id], FilterSERVICE_CONTROL_POLICY )[Policies] } assert ProtectLogs in names, f{acc[account_name]} 缺少 ProtectLogs测试的粒度按“一条需求一个断言”来切不要写一个巨型测试把所有检查塞进去失败时看不出是哪条需求破了。scopesession让账号清单只读一次减少重复 IO。实际项目里还会加一个conftest.py统一创建 boto3 客户端并指定只读凭证验证层用只读角色跑避免测试脚本误改环境——这个习惯能省掉很多后悔药。5. 避坑与常见问题上线前必须排查的五件事5.1 SCP 不生效先看传播延迟和挂载目标现象是策略挂上去了但目标账号里高危操作还能做。原因通常有两个一是 SCP 变更到生效有传播延迟刚挂完立刻测会误判二是挂错了目标SCP 挂在 OU 上只影响其下账号挂在根上才全局生效而list_policies_for_target返回的是直接挂载不含继承。解决方法是等几分钟再测并且验证时同时检查祖先 OU 的挂载链写一个effective_policies函数把从根到当前节点的策略并集算出来断言基于并集而不是直接挂载。5.2 账号创建卡在 IN_PROGRESS配额与邮箱重复现象是批量建号时部分请求长时间不返回或直接 FAILED。原因多是邮箱重复包括大小写不同但实际相同、并发超过配额、或账号名含非法字符。解决方法是建号前统一做邮箱规范化转小写、去空格并查重串行执行加退避失败原因从FailureReason里读出来记日志。别用多线程硬冲配额被限流后重试窗口很难估。5.3 日志桶写入失败桶策略条件写错现象是跟踪状态显示 logging但桶里没对象。原因通常是桶策略的SourceArn区域或账号写错或者桶在另一个区域。解决方法是先用aws cloudtrail get-trail-status看LatestDeliveryError再对照桶策略条件逐字段核对。我一般会在验证层加一条断言直接检查桶策略里是否存在允许 CloudTrail 写入的语句把问题挡在交付前。5.4 脚本重跑把手工例外冲掉现象是重跑同步脚本后某个临时放开的策略被摘了。原因是脚本把“期望状态”当成唯一真相没给人工资格留口子。解决方法是在映射表里给例外策略加manual标记同步时跳过或者把例外写进单独的覆盖文件脚本合并时以覆盖文件为准。核心原则是自动化脚本可以管理大部分状态但必须显式承认人工干预的存在。5.5 验证层用错凭证只读测试改了环境现象是跑测试时意外触发了资源变更。原因是测试和实现共用了一套高权限凭证。解决方法是给验证层单独建只读角色测试代码里显式assume_role并在 CI 里用这套凭证跑。这个坑踩一次就够记一辈子测试脚本的权限应该比实现脚本更小而不是更大。6. 验收与答辩怎么证明这套 Landing Zone 真的立住了验收的核心不是“代码跑通了”而是“每条需求都有对应的证据”。我一般按三层来组织验收材料。第一层是结构证据账号清单和 OU 树导出的快照证明账号归属符合设计答辩时直接展示ou_path和实际 OU 的对照表。第二层是策略证据对每个 OU 导出有效策略并集和生产账号的实测拒绝结果比如尝试关闭跟踪被拒的截图或日志证明护栏真的拦得住。第三层是测试报告pytest 全绿的报告加上覆盖率说明验证层覆盖了哪些需求哪些是已知不覆盖的边界。具体操作上我会准备一张验收对照表左边列需求编号中间列验证方法右边列证据位置。比如“生产账号禁止删除日志桶”对应测试test_prod_has_protect_logs_scp加一次手工拒绝演示“组织级跟踪覆盖所有账号”对应跟踪状态查询和桶内按账号前缀的对象抽样。答辩时按这张表走评委问哪条就翻到哪条比从头讲架构高效得多。需求验证方法证据账号归属正确清单与 OU 树比对快照 JSON生产护栏生效有效策略并集断言 实测拒绝测试报告 操作日志日志集中归档跟踪状态 桶对象抽样查询输出跨账号网络连通对等连接状态 连通性测试测试记录还有一个答辩技巧主动讲一个你踩过的坑和怎么修的。比如 SCP 传播延迟导致第一次验收误判后来在验证脚本里加了等待和并集计算。评委对“知道边界在哪”的印象往往比“功能全做完”更深。从那以后我每次交付这类方案都强制先跑一遍只读验证层再演示变更确保演示环境的状态和测试报告一致。希望这套结构能帮到你照着改就能交出一份站得住的方案。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?