云主机与资源管理:审阅实例、磁盘与角色变更
本篇以EC2 资源审阅为例;前置是账户、区域、实例/卷身份与 IAM 角色。源码与输入与下文配置共用同一份文件,示例未执行。
核对日期2026-10-17;适用范围:以AWS EC2 2016-11-15 API模型与IAM概念为例,并对照阿里云ECS中文生命周期说明;云控制台与服务特性按2026-10-17核对,不声称跨云接口等价。示例未执行;真实环境仍需验证产品配置、权限、连通与恢复结果。先读起点理解前置,再按章节完成推演。
清单与请求格式
云资源审阅文件包括review.py、inventory.json、request.json、iam-policy.json与requirements.txt。示例未执行,无云SDK或凭据;000000000000、i-teaching-only和teaching-region是显式假身份,不能提交真实接口。程序审阅保存清单中的owner、状态、暴露和删除前置,输出executed=false;它不是云模拟器,也不会检查线上快照存在或真实权限。
删除前置和拒绝行为
云资源删除审批在课堂请求中用reviewed-lab-only标记,程序同时要求备份字段、所有者一致、无publicIp及允许的当前状态。任一缺失退出2,输出错误而不返回成功报告;该字符串仅作输入合同,绝不是真实组织审批或授权凭据。真实终止前要另外核对工单、资源依赖和备份可恢复性,不能靠修改JSON审批值绕过流程。
输出与失败推演
云资源审阅预期正常输出proposedAction=terminate、executed=false、rootDiskAtRisk=true,提醒root卷随终止删除。把approval改为unreviewed、移除backup或改变owner应拒绝。程序只读,不会创建或释放实例,清理仅删除读者自行生成的副本和报告。一次本地通过不说明费用已停止、资源处于stopped或持有云账号权限。
完整EC2资源模板与生命周期边界
instance.yaml是CloudFormation 2010-09-09格式,完整声明EC2实例、只读IAM角色及实例配置文件;要求读者提供可信x86_64 EBS AMI、同VPC隔离子网/安全组、真实RootDeviceName和不小于镜像快照的卷大小。模板强制无公网IP、IMDSv2及加密gp3根卷,DeleteOnTermination=false保留根盘;这与审阅fixture的rootDeleteOnTermination=true是刻意对照,不可混用。角色仅有Describe,不含管理员/上传/远程管理权限,也未提供SSH密钥或应用部署。参数引用不是已存在资源的证明,不能直接宣称可部署。
读者以后可用自己的CloudFormation流程先创建change set、审阅IAM能力与替换/删除范围,再执行专用stack。成功预期只是私有实例和角色形成关联,不代表有登录通道或应用。错误AMI架构、根设备名、卷大小、配额、同VPC约束或权限将失败;更新替换可能创建新实例并保留旧卷。清理应先保全数据,再删除专用stack并核查保留EBS、快照与其他依赖仍可能计费,不能把stack删除当作全费用归零。
AWSTemplateFormatVersion: '2010-09-09'
Description: Teaching EC2 resource template; not deployed; requires reader-owned isolated inputs.
Parameters:
ImageId:
Type: AWS::EC2::Image::Id
Description: Reader-owned trusted x86_64 HVM EBS-backed Linux AMI in this region.
SubnetId:
Type: AWS::EC2::Subnet::Id
Description: Reader-owned isolated subnet with reviewed routes and no public mapping.
SecurityGroupId:
Type: AWS::EC2::SecurityGroup::Id
Description: Reader-owned same-VPC security group with reviewed ingress and egress.
RootDeviceName:
Type: String
Description: Actual root device name from the selected AMI; no guessed default.
RootVolumeGiB:
Type: Number
MinValue: 8
Description: At least the AMI root snapshot size; reader must inspect before choosing.
Resources:
InventoryRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: DescribeInventoryOnly
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: [ec2:DescribeInstances, ec2:DescribeVolumes]
Resource: '*'
InventoryProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- Ref: InventoryRole
TeachingInstance:
Type: AWS::EC2::Instance
Properties:
ImageId:
Ref: ImageId
InstanceType: t3.micro
IamInstanceProfile:
Ref: InventoryProfile
MetadataOptions:
HttpTokens: required
HttpEndpoint: enabled
HttpPutResponseHopLimit: 1
NetworkInterfaces:
- DeviceIndex: '0'
AssociatePublicIpAddress: false
SubnetId:
Ref: SubnetId
GroupSet:
- Ref: SecurityGroupId
BlockDeviceMappings:
- DeviceName:
Ref: RootDeviceName
Ebs:
Encrypted: true
VolumeType: gp3
VolumeSize:
Ref: RootVolumeGiB
DeleteOnTermination: false
Tags:
- Key: Name
Value: m5-teaching-only
- Key: owner
Value: learning
Outputs:
InstanceId:
Value:
Ref: TeachingInstance
PrivateIp:
Value:
Fn::GetAtt: [TeachingInstance, PrivateIp]完整源码、配置与依赖
下列文件与README共用同一份仓库内容,保持输入、失败与清理合同一致。示例未执行;以下命令仅供读者在满足前置后自行运行。
requirements.txt
# Python >=3.11,<3.14; standard library only; no cloud SDKinventory.json
{
"account": "000000000000",
"region": "teaching-region",
"instances": [
{
"id": "i-teaching-only",
"state": "stopped",
"owner": "learning",
"role": "readonly-inventory",
"publicIp": null,
"rootDeleteOnTermination": true,
"backup": "snapshot-teaching-only"
}
]
}request.json
{
"instance": "i-teaching-only",
"action": "terminate",
"owner": "learning",
"approval": "reviewed-lab-only"
}iam-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "InventoryReadOnly",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeVolumes"
],
"Resource": "*"
}
]
}review.py
"""Review a saved inventory/request. Never calls a cloud API or changes a VM."""
import argparse
import json
import sys
from pathlib import Path
def review(inventory, request):
if not isinstance(inventory, dict) or not isinstance(request, dict):
raise ValueError("inventory and request must be objects")
if not isinstance(inventory.get("instances"), list) or any(not isinstance(vm, dict) for vm in inventory["instances"]):
raise ValueError("instances must be a list of objects")
if request.get("action") not in {"start", "stop", "terminate"}:
raise ValueError("unsupported lifecycle action")
matches = [vm for vm in inventory["instances"] if vm["id"] == request["instance"]]
if len(matches) != 1:
raise ValueError("expected one inventory match")
vm = matches[0]
if type(vm.get("rootDeleteOnTermination")) is not bool:
raise ValueError("rootDeleteOnTermination must be an explicit boolean")
if vm["owner"] != request.get("owner") or vm.get("publicIp"):
raise ValueError("owner mismatch or public exposure")
if request["action"] == "terminate" and (not vm.get("backup") or request.get("approval") != "reviewed-lab-only"):
raise ValueError("termination requires backup evidence and lab review")
allowed = {"start": {"stopped"}, "stop": {"running"}, "terminate": {"running", "stopped"}}
if vm["state"] not in allowed[request["action"]]:
raise ValueError("state does not satisfy action precondition")
return {"instance": vm["id"], "proposedAction": request["action"], "executed": False,
"rootDiskAtRisk": request["action"] == "terminate" and vm["rootDeleteOnTermination"]}
def main():
parser = argparse.ArgumentParser()
parser.add_argument("inventory", type=Path)
parser.add_argument("request", type=Path)
args = parser.parse_args()
try:
result = review(json.loads(args.inventory.read_text(encoding="utf-8")), json.loads(args.request.read_text(encoding="utf-8")))
except (OSError, ValueError, KeyError, TypeError) as error:
print(str(error), file=sys.stderr)
return 2
print(json.dumps(result, ensure_ascii=False))
return 0
if __name__ == "__main__":
raise SystemExit(main())README.md
# 云资源离线变更审阅
示例未执行。Python3.11–3.13标准库,无SDK、凭据或云CLI;这是保存清单的审阅器,不是云模拟器,也不能执行生命周期动作。inventory.json/request.json是自定义输入格式,不是EC2 API请求。000000000000、teaching-region、i-teaching-only、snapshot-teaching-only都是假身份,读者连接真实平台前必须自行替换并重新核实权限、备份和区域。
读者在此目录自跑 `python review.py inventory.json request.json`,预期退出0,返回proposedAction=terminate、executed=false、rootDiskAtRisk=true。通过仅说明本地清单通过简化检查,绝不表示拥有删除权限、快照存在或真实资源安全。iam-policy.json是AWS EC2只读身份策略;Describe动作按其授权特性使用Resource=*,不能添加终止权限。该JSON本身不是完整可挂载角色;另附instance.yaml给出角色信任和实例配置文件的完整示例,读者仍须按账户边界审核。
失败推演:复制request为独立副本,将approval改成unreviewed,预期退出2;删除inventory副本中的backup同样拒绝;owner不匹配、publicIp存在、state不满足动作前置也拒绝。不要修改基线fixtures以伪造审批。备份只检查字段存在,真实快照恢复必须另作实际验证。
恢复清理:程序只读文件并向stdout输出,无云资源需要清理;删除自行创建的副本与报告。真实云的停止仍可能产生磁盘/地址费用,终止是否删除磁盘取决于DeleteOnTermination,备份与共享卷需单独处理。真实终止不能用“重启”撤回,应从已验证备份创建新实例、重绑访问并核对数据。不得执行本文中的云动作来验收笔记。
## 完整资源配置
instance.yaml是CloudFormation 2010-09-09格式,完整声明EC2实例、只读IAM角色及实例配置文件;要求读者提供可信x86_64 EBS AMI、同VPC隔离子网/安全组、真实RootDeviceName和不小于镜像快照的卷大小。模板强制无公网IP、IMDSv2及加密gp3根卷,DeleteOnTermination=false保留根盘;这与审阅fixture的rootDeleteOnTermination=true是刻意对照,不可混用。角色仅有Describe,不含管理员/上传/远程管理权限,也未提供SSH密钥或应用部署。参数引用不是已存在资源的证明,不能直接宣称可部署。
读者以后可用自己的CloudFormation流程先创建change set、审阅IAM能力与替换/删除范围,再执行专用stack;本轮未创建change set或调用任何API。成功预期只是私有实例和角色形成关联,不代表有登录通道或应用。错误AMI架构、根设备名、卷大小、配额、同VPC约束或权限将失败;更新替换可能创建新实例并保留旧卷。清理应先保全数据,再删除专用stack并核查保留EBS、快照与其他依赖仍可能计费,不能把stack删除当作全费用归零。
[CloudFormation EC2属性](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-instance.html)本篇练习与参考解答
练习:离线审阅通过是否说明CloudFormation模板能够部署?
参考解答
不能。JSON审阅不验证真实AMI、区域、卷大小、配额或IAM,完整模板还需读者提供自有输入并审阅change set。
核对资料与相邻阅读
继续阅读
- 知识专题
- 知识专题
- 知识专题