Terraform自动化部署SSM就绪的EC2实例
1. 项目概述为什么一个能自动装好SSM代理的EC2实例值得你花两小时认真读完我第一次在AWS控制台里点出第17个EC2实例时手已经有点抖了。不是因为紧张是因为重复劳动带来的生理疲惫——选AMI、挑区域、配安全组、贴标签、挂IAM角色、再手动连上去跑一遍yum install amazon-ssm-agent……结果发现安全组少开了一个端口回退重来或者忘了给实例打上Environmentdev标签导致第二天运维同事在资源治理平台里找不到它最绝的一次是我在法兰克福区创建的实例被另一个同事误以为在东京区他直接在控制台里删掉了“不存在”的资源而我的Terraform状态文件还躺在本地硬盘上没人知道发生了什么。这种场景你肯定不陌生。它不是“偶尔手滑”而是手工操作基础设施的必然宿命不可复现、不可审计、不可协作、不可回滚。你写的每一条aws ec2 run-instances命令都像在沙子上刻字你点下的每一个“Launch Instance”按钮都在为下一次故障埋雷。而Terraform要做的不是让你多学一个工具而是帮你把“基础设施”从一种临时的、易变的操作行为变成一种可版本化、可审查、可测试、可交付的工程产物。这篇教程聚焦一个非常具体、非常高频、也非常有代表性的任务用Terraform自动化部署一台预装SSMSystems Manager代理的EC2实例。它看起来只是“多装一个agent”但背后是一整套现代云基础设施交付范式的落地切口。你将亲手写出能立刻跑通的代码而不是看一堆概念图你会理解为什么user_data里那几行bash脚本必须这么写而不是复制粘贴就完事你会搞懂terraform.tfstate文件到底存了什么、为什么不能放本地、S3锁文件是怎么防止两人同时apply把环境搞崩的你还会把硬编码的t2.micro和eu-central-1抽成变量再进一步打包成模块为后续部署10台、100台同类服务器铺平道路。它适合三类人刚考完AWS Cloud Practitioner、想把知识落地的新人已经在用AWS但还在控制台里“点点点”的中级工程师以及团队里正被“环境不一致”问题折磨得夜不能寐的技术负责人。不需要你精通Go或Python只需要你愿意把鼠标从AWS控制台挪开5分钟打开终端敲下terraform init。接下来的内容没有一句是“理论上可以”全是我在生产环境踩过坑、改过三次配置、重试过七遍才确认下来的实操细节。2. 整体设计思路为什么是这个结构为什么不是Ansible或CloudFormation2.1 核心目标拆解我们到底要“自动化”什么很多人一上来就想“我要用Terraform管整个VPC”结果三天后卡在子网路由表关联上信心全无。真正的起点永远是一个最小、可验证、有明确业务价值的闭环。本项目的目标非常聚焦交付一个EC2实例这是AWS最基础、最通用的计算单元所有上层服务数据库、缓存、应用服务器都跑在它上面。该实例必须能被SSM管理这意味着无需SSH密钥、无需开放22端口、无需维护跳板机就能执行命令、推送脚本、查看日志、安装补丁——这是现代云运维的“黄金标准”。整个过程必须100%代码化、零人工干预从创建IAM角色到启动实例全部由Terraform驱动且每次apply的结果完全一致。这三个目标环环相扣。如果只部署EC2但没SSM你还是得登录如果有了SSM但依赖手动创建IAM角色那“自动化”就打了折扣如果每次部署都要改region、改instance_type那它就无法复用。所以我们的架构设计必须围绕这三点展开而不是堆砌功能。2.2 方案选型为什么是Terraform而不是其他工具市面上能干这事的工具不少但选择Terraform不是因为它“流行”而是它在几个关键维度上给出了最优解声明式 vs. 命令式Ansible是命令式“先做A再做B最后检查C”它擅长配置操作系统但对“创建一个带特定策略的IAM角色”这种云原生资源语法冗长且易出错。Terraform是声明式“我要一个东西它长这样”你只描述终态它自己算依赖、排顺序、处理失败。比如你定义了aws_instance依赖aws_iam_roleTerraform会自动确保角色创建成功后才启动实例失败时能精准告诉你卡在哪一步。我试过用Ansible的aws_ec2模块做同样事光是IAM角色的信任策略JSON格式就调了40分钟。状态管理能力CloudFormation也能声明式部署但它把状态牢牢锁死在AWS账户里你无法用Git管理变更历史也无法做跨账户同步。Terraform的tfstate文件是纯文本JSON格式你可以把它放进GitLab仓库设置分支保护规则要求所有apply前必须经过Code Review。去年我们一个客户就靠这个功能在一次误删生产RDS的事故中5分钟内从Git历史里找回了旧状态并terraform apply回滚。模块化与复用性CloudFormation的嵌套栈Nested Stack配置复杂调试困难Ansible的Role虽然可复用但缺乏强类型校验。Terraform的模块Module是语言级特性支持输入变量类型约束type string、默认值、描述文档还能在Registry里直接引用HashiCorp官方或社区维护的成熟模块比如terraform-aws-modules/vpc/aws。我们后面会把EC2SSM打包成模块你以后要部署K8s节点、CI/CD构建机、甚至GPU训练实例只要换掉user_data里的安装命令其余代码一行不用改。Provider生态与AWS深度集成Terraform AWS Provider目前支持超过300个AWS服务资源且更新速度紧跟AWS新发布比如2023年推出的EC2 Image BuilderProvider在两周内就提供了aws_imagebuilder_component资源。更重要的是它对AWS特有的概念有原生支持data aws_ami能跨Region查官方AMIaws_iam_policy_document能用HCL生成合规的JSON策略aws_ssm_parameter能无缝对接Parameter Store——这些都不是“能用”而是“用起来比AWS CLI还顺手”。提示不要陷入“工具之争”。Ansible在OS层配置上依然无敌CloudFormation在纯AWS、强合规场景下有其优势。Terraform的核心价值是成为你云基础设施的“统一API层”——上接CI/CD下管多云中间串起所有配置工具。把它当成你的“基础设施操作系统”而不是一个“又一个CLI”。2.3 架构分层为什么坚持main/variables/outputs三分法新手常犯的错误是把所有东西都塞进一个main.tf里。我见过最夸张的案例一个500行的main.tf文件里混着provider配置、AMI查询、EC2定义、安全组、输出还有硬编码的密码。这种代码别说协作连自己三天后都看不懂。我们采用经典的三层分离不是为了“看起来专业”而是解决三个真实痛点variables.tf隔离变化点环境dev/staging/prod、区域us-east-1/eu-west-1、规格t2.micro/t3.medium这些参数是系统中最容易变、也最需要被不同角色控制的部分。开发人员改terraform-dev.tfvars运维人员管terraform-prod.tfvars安全团队审核variables.tf里的description字段是否准确。把它们集中管理等于给系统装上了“可调节旋钮”而不是每次都要动手术刀。main.tf专注资源编排逻辑这里只放“怎么做”哪些资源要创建、它们之间如何关联、数据源如何查询。它应该像一份清晰的乐谱告诉Terraform“这段旋律需要小提琴EC2、大提琴IAM Role和定音鼓SSM Policy一起演奏”。去掉所有硬编码值只留var.xxx占位符让逻辑本身变得纯粹、可测试、可推理。outputs.tf定义交付物契约terraform apply完成后你真正需要的是什么是实例ID是公网IP是SSM会话连接字符串outputs.tf就是这份“交付清单”。它强制你思考“我的这个模块对外承诺提供哪些信息” 这些输出值会被上层模块或CI/CD流水线直接引用。比如你的部署脚本可以用$(terraform output -raw instance_id)获取ID然后调用aws ssm start-session建立连接——整个流程无需人工抄写。这种分层本质上是在模拟软件工程的最佳实践高内聚、低耦合。它让代码具备了“可演进性”。当业务需要增加CloudWatch日志组时你只需在main.tf里加一个aws_cloudwatch_log_group资源在outputs.tf里加一个log_group_name输出variables.tf里加一个log_retention_days变量——改动范围清晰风险可控。3. 核心细节解析从AMI查询到SSM代理安装的每一处陷阱3.1 AMI数据源为什么不能写死ami-0abcdef1234567890在AWS里AMIAmazon Machine ImageID是Region级的。同一个Amazon Linux 2 AMI在us-east-1的ID是ami-0c55b159cbfafe1f0在eu-central-1可能是ami-0a313d60987694e48。如果你在main.tf里硬编码一个ID代码就彻底失去了跨Region能力更别提复用。Terraform的data aws_ami就是为解决这个问题而生。但它的配置远不止“查最新版”这么简单这里有三个极易被忽略的关键点most_recent true的真实含义它不是“找发布时间最新的AMI”而是“在满足所有filter条件的AMI中选creation_date字段值最大的那个”。而Amazon官方AMI的creation_date是镜像构建完成的时间不是上传时间。这意味着即使你昨天刚用Packer构建了一个自定义AMI只要它的creation_date比官方AMI老most_recent true还是会选中官方镜像。所以永远不要依赖most_recent来获取你自己的AMI。对于自定义AMI必须用name或tags精确匹配。filter的组合逻辑是AND不是OR你写了两个filter块Terraform会把它们当作“同时满足”的条件。比如filter { name name values [amzn2-ami-hvm-*-x86_64-gp2] } filter { name virtualization-type values [hvm] }这等价于SQL里的WHERE name LIKE amzn2-ami-hvm%-x86_64-gp2 AND virtualization-type hvm。如果漏掉virtualization-type你可能会意外匹配到旧的PVParavirtual类型AMI而t2.micro等现代实例类型只支持HVM会导致启动失败。owners [amazon]是安全底线AMI可以由任何人创建并公开分享。如果不指定ownersdata块可能返回社区用户上传的、包含挖矿木马的恶意AMI。[amazon]强制限定只搜索AWS官方发布的镜像。生产环境必须加上这一行这是基础设施安全的第一道门禁。实操心得我曾经在一个客户环境里因为忘记加owners [amazon]terraform plan返回了一个ID为ami-0123456789abcdef0的AMIapply后实例启动失败。terraform show显示ami值是空的但错误日志里只有一句模糊的InvalidAMIID.NotFound。花了两小时排查才发现这个AMI属于一个叫public-images的第三方账户早已被删除。从此我的所有data aws_ami都加了owners并养成了在plan输出里第一眼就核对ami值是否以ami-开头的习惯。3.2 IAM角色与实例配置文件权限最小化的实战推演让EC2实例能被SSM管理核心是赋予它AmazonSSMManagedInstanceCore托管策略。但直接把策略绑给实例是严重违反最小权限原则的。正确的路径是IAM Role → Instance Profile → EC2 Instance。这个链条里每个环节都有讲究Role的assume_role_policy必须精确匹配EC2服务主体很多人复制网上的例子把Principal.Service写成ec2.amazonaws.com这在大多数Region是OK的但在cn-north-1中国宁夏或us-gov-west-1AWS GovCloud等特殊Region服务主体名是ec2.amazonaws.com.cn或ec2.us-gov.amazonaws.com。硬编码会导致terraform apply失败。最佳实践是使用data aws_partition动态获取data aws_partition current {} resource aws_iam_role ssm_role { name ec2-ssm-role assume_role_policy jsonencode({ Version 2012-10-17 Statement [{ Action sts:AssumeRole Effect Allow Principal { Service ec2.${data.aws_partition.current.dns_suffix} } }] }) }aws_partition数据源会根据当前Provider配置的Region自动返回aws、aws-cn或aws-us-govdns_suffix则给出对应的域名后缀。这保证了代码在全球所有AWS Region都能开箱即用。Instance Profile是Role的“包装纸”必须显式创建你不能直接在aws_instance资源里写iam_role aws_iam_role.ssm_role.name。AWS API要求EC2实例绑定的必须是一个InstanceProfile而InstanceProfile内部再关联Role。Terraform的aws_iam_instance_profile资源就是干这个的。漏掉这一步apply会报错InvalidParameterValue: Value (ec2-ssm-role) for parameter iamInstanceProfile.name is invalid。iam_instance_profile字段名易混淆在aws_instance资源里绑定实例配置文件的字段叫iam_instance_profile不是iam_role或instance_profile。这是一个历史遗留的命名很多新手在这里栽跟头。记住口诀“实例Profile不是Role”。注意AmazonSSMManagedInstanceCore策略本身已足够精简它只授予SSM Agent连接、发送心跳、执行命令等必要权限。但如果你的应用还需要访问S3日志桶或DynamoDB配置表不要把那些权限也加到这个Role里。应该为应用创建独立的Role通过aws_iam_role_policy_attachment附加并在user_data里用aws configure切换凭证——让SSM管理权和应用运行权彻底分离。3.3 user_data脚本为什么用sudo yum install -y而不是curl | bashuser_data是EC2启动时执行的初始化脚本它是整个自动化链条里最脆弱的一环。任何网络超时、包管理器锁、权限错误都会导致SSM Agent安装失败而实例本身却显示“Running”。你只能干瞪眼等着aws ssm describe-instance-information返回空结果。我们选择yum install而非curl | bash基于三个硬核理由可审计性与确定性https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm这个URL指向的是“latest”版本。今天下载的是3.2.1234.0明天AWS发布新版它就变成3.2.1235.0。curl | bash会静默升级可能引入不兼容变更。而.rpm包是确定性产物你可以用yum list installed | grep amazon-ssm-agent精确验证版本。生产环境必须锁定版本latest是魔鬼。离线安装能力yum install会把RPM包下载到/var/cache/yum下次重装同一版本时直接复用不依赖外网。而curl必须实时下载一旦S3桶临时不可达极罕见但可能安装就失败。我们曾在一个金融客户内网环境里因防火墙策略收紧curl被拦截yum install却畅通无阻。错误处理与日志沉淀yum install -y的退出码是标准的0成功1失败。你可以在脚本末尾加echo $?记录到/var/log/user-data.log。而curl | bash的错误流完全不可控bash可能在curl还没结束时就开始执行半截脚本导致难以诊断。实操心得我建议你在user_data里加入完整的错误捕获和日志#!/bin/bash exec /var/log/user-data.log 21 set -xeu cd /tmp echo [$(date)] Starting SSM Agent installation... /var/log/user-data.log # 下载RPM包带重试 for i in {1..3}; do if curl -f -s -o amazon-ssm-agent.rpm https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm; then echo [$(date)] RPM download succeeded on attempt $i /var/log/user-data.log break else echo [$(date)] RPM download failed on attempt $i, retrying... /var/log/user-data.log sleep 5 fi done # 安装并启用 yum install -y ./amazon-ssm-agent.rpm systemctl enable amazon-ssm-agent systemctl start amazon-ssm-agent echo [$(date)] SSM Agent installation completed successfully. /var/log/user-data.logset -xeu确保任何命令失败立即退出exec /var/log/user-data.log把所有输出重定向到日志for循环实现下载重试。这些细节决定了你是在“部署一个实例”还是在“部署一个可运维的实例”。4. 实操全流程从零开始一步步敲出可运行的代码4.1 环境准备CLI安装与凭证配置的避坑指南4.1.1 Terraform安装版本选择的血泪教训Terraform的版本迭代很快但并非越新越好。截至2024年强烈推荐使用v1.5.x系列如1.5.7原因如下v1.6 引入了新的required_providers语法强化要求所有Provider必须在required_providers块中明确定义source和version否则init失败。而大量线上教程、模块、甚至HashiCorp官方文档仍以v1.5为基准。如果你用v1.6会频繁遇到Provider registry.terraform.io/hashicorp/aws v6.0.0 is not compatible with Terraform 1.6.0这类报错。v1.4及更早版本缺少use_lockfile true的原生S3锁支持你必须额外配置DynamoDB徒增复杂度。安装步骤以macOS为例# 卸载旧版本如果存在 brew uninstall terraform # 添加HashiCorp官方tap避免用homebrew-core里可能滞后的版本 brew tap hashicorp/tap # 安装v1.5.7指定版本号杜绝意外升级 brew install hashicorp/tap/terraform1.5 # 创建软链接让terraform命令指向v1.5 brew link --force terraform1.5 # 验证 terraform version # 输出应为Terraform v1.5.7 # on darwin_arm64提示Windows用户请务必从 HashiCorp官网下载v1.5.7的zip包 解压后将terraform.exe所在目录加入PATH。不要用choco install terraformChocolatey默认安装最新版且无法指定历史版本。4.1.2 AWS凭证三种方式的安全等级排序凭证配置是安全红线必须严格遵循最小权限。以下是三种方式的安全等级从高到低IAM Role for EC2最高安全适用于Terraform代码运行在EC2实例上的场景如CI/CD Runner。你为该EC2实例附加一个IAM RoleRole拥有AdministratorAccess或精细化权限。Terraform自动从实例元数据服务IMDS获取临时凭证无需存储长期密钥。这是生产环境首选。AWS Credentials File开发/测试推荐创建~/.aws/credentials文件[default] aws_access_key_id AKIAIOSFODNN7EXAMPLE aws_secret_access_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY关键禁忌这个文件绝对不能提交到Git必须加入.gitignore。且IAM用户必须是专门创建的绝不使用Root账号。权限策略应限制为仅允许操作本项目所需资源如ec2:*,iam:CreateRole,iam:AttachRolePolicy等而非AdministratorAccess。Environment Variables临时调试用export AWS_ACCESS_KEY_IDAKIAIOSFODNN7EXAMPLE export AWS_SECRET_ACCESS_KEYwJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY export AWS_DEFAULT_REGIONeu-central-1优点是灵活缺点是容易泄露ps aux可见进程环境变量。严禁在生产脚本或CI/CD中使用。实操心得我给自己设了一条铁律——本地开发用Credentials FileCI/CD用IAM Role。并且我创建了一个名为terraform-deployer的IAM用户其策略精确到{ Version: 2012-10-17, Statement: [ { Effect: Allow, Action: [ ec2:RunInstances, ec2:DescribeImages, ec2:DescribeSecurityGroups, ec2:CreateTags, iam:CreateRole, iam:AttachRolePolicy, iam:CreateInstanceProfile, iam:AddRoleToInstanceProfile, s3:GetObject, s3:ListBucket ], Resource: * } ] }这个策略只允许创建本教程所需的资源连ec2:TerminateInstances都没有最大程度降低误操作风险。4.2 项目初始化创建标准目录结构与基础文件在终端中执行以下命令创建项目骨架mkdir -p aws-terraform/modules/ec2-with-ssm cd aws-terraform # 创建根目录文件 touch main.tf variables.tf outputs.tf # 创建模块目录文件 touch modules/ec2-with-ssm/{main.tf,variables.tf,outputs.tf} # 初始化Git强烈建议状态文件必须版本化 git init echo *.tfstate .gitignore echo .terraform/ .gitignore echo terraform.tfstate.backup .gitignore git add .gitignore git commit -m chore: init git repo, ignore state files现在你的目录结构是aws-terraform/ ├── main.tf ├── variables.tf ├── outputs.tf └── modules/ └── ec2-with-ssm/ ├── main.tf ├── variables.tf └── outputs.tf4.2.1 编写variables.tf定义可配置的“旋钮”在variables.tf中填入以下内容。注意description字段它会在terraform plan时作为注释显示是给协作者最好的文档# variables.tf variable aws_region { description AWS region where resources will be created. Must match the S3 backend region. type string default eu-central-1 } variable instance_type { description EC2 instance type (e.g., t2.micro, t3.medium). type string default t2.micro } variable environment { description Environment name (e.g., dev, staging, prod). Used for tagging. type string default dev } variable key_name { description Name of the EC2 Key Pair to enable SSH access (optional, leave empty if not needed). type string default } variable enable_ssm { description Whether to install and configure SSM Agent on the instance. type bool default true }4.2.2 编写main.tf根模块的Provider与Backend配置main.tf是项目的入口。这里只放全局配置不放具体资源# main.tf terraform { required_providers { aws { source hashicorp/aws version ~ 5.0 # 锁定5.x系列兼容v1.5.x Terraform } } # S3 Backend配置 - 生产环境必须启用 backend s3 { bucket your-unique-bucket-name-here # 替换为你自己的S3桶名 key terraform.tfstate region eu-central-1 # 必须与aws_region变量值一致 dynamodb_table terraform-state-lock # 旧版锁表v1.5可删但保留兼容 encrypt true } } # AWS Provider配置 provider aws { region var.aws_region } # 模块调用 module web_server { source ./modules/ec2-with-ssm aws_region var.aws_region instance_type var.instance_type environment var.environment key_name var.key_name enable_ssm var.enable_ssm }注意bucket字段必须是你自己创建的S3桶名且该桶必须存在、已启用版本控制。region必须与aws_region变量值严格一致否则init会失败。4.2.3 编写模块modules/ec2-with-ssm/main.tf核心资源定义这是真正的“干活”文件。将之前讨论的所有最佳实践融入其中# modules/ec2-with-ssm/main.tf # 输入变量必须与根模块的output保持一致 variable aws_region { description AWS region type string } variable instance_type { description EC2 instance type type string } variable environment { description Environment tag type string } variable key_name { description EC2 Key Pair name type string default } variable enable_ssm { description Enable SSM Agent installation type bool default true } # 数据源动态获取AMI data aws_partition current {} data aws_ami amazon_linux { most_recent true filter { name name values [amzn2-ami-hvm-*-x86_64-gp2] } filter { name virtualization-type values [hvm] } owners [amazon] } # IAM Role for SSM resource aws_iam_role ssm_role { name ec2-ssm-role-${var.environment} assume_role_policy jsonencode({ Version 2012-10-17 Statement [{ Action sts:AssumeRole Effect Allow Principal { Service ec2.${data.aws_partition.current.dns_suffix} } }] }) } # Attach SSM managed policy resource aws_iam_role_policy_attachment ssm_policy { role aws_iam_role.ssm_role.name policy_arn arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonSSMManagedInstanceCore } # Instance Profile resource aws_iam_instance_profile ssm_profile { name ec2-ssm-profile-${var.environment} role aws_iam_role.ssm_role.name } # EC2 Instance resource aws_instance web_server { ami data.aws_ami.amazon_linux.id instance_type var.instance_type key_name var.key_name # 只有enable_ssm为true时才绑定SSM Profile dynamic iam_instance_profile { for_each var.enable_ssm ? [1] : [] content { name aws_iam_instance_profile.ssm_profile.name } } # user_data条件化执行SSM安装 user_data var.enable_ssm ? -EOF #!/bin/bash exec /var/log/user-data.log 21 set -xeu cd /tmp echo [$(date)] Starting SSM Agent installation... for i in {1..3}; do if curl -f -s -o amazon-ssm-agent.rpm https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm; then echo [$(date)] RPM download succeeded on attempt $i break else echo [$(date)] RPM download failed on attempt $i, retrying... sleep 5 fi done yum install -y ./amazon-ssm-agent.rpm systemctl enable amazon-ssm-agent systemctl start amazon-ssm-agent echo [$(date)] SSM Agent installation completed successfully. EOF : tags { Name TerraformWebServer-${var.environment} Environment var.environment } }4.2.4 编写modules/ec2-with-ssm/outputs.tf定义模块输出契约模块的输出是它对外提供的“接口”。其他模块或脚本只应该依赖这些输出而不应深入main.tf内部# modules/ec2-with-ssm/outputs.tf output instance_id { description The ID of the EC2 instance value aws_instance.web_server.id } output public_ip { description The public IP address of the EC2 instance value aws_instance.web_server.public_ip } output private_ip { description The private IP address of the EC2 instance value aws_instance.web_server.private_ip } output ssm_instance_id { description The SSM-managed instance ID (same as instance_id) value aws_instance.web_server.id }4.2.5 编写根outputs.tf汇总最终交付物在根目录的outputs.tf中暴露模块的输出方便上层调用# outputs.tf output web_server_instance_id { description EC2 instance ID from the web_server module value module.web_server.instance_id } output web_server_public_ip { description Public IP of the web server instance value module.web_server.public_ip } output web_server_ssm_id { description SSM instance ID for session management value module.web_server.ssm_instance_id }4.3 执行Terraform工作流init/plan/apply的完整命令链一切就绪进入激动人心的执行阶段。请严格按照顺序操作4.3.1 初始化下载Provider并配置Backend# 在aws-terraform/目录下执行 terraform init # 首次使用S3 Backend时会提示 # Do you want to copy existing state to the new backend? # 输入 yesTerraform会将本地的terraform.tfstate如果存在上传到S34.3.2 计划预览在真实世界前先看一眼蓝图# 生成执行计划 terraform plan -var-fileterraform-dev.tfvars # 如果你还没有terraform-dev.tfvars先创建它 echo aws_region eu-central-1 terraform-dev.tfvars echo instance_type t2.micro terraform-dev.tfvars echo environment dev terraform-dev.tfvarsplan输出会详细列出所有将被创建的资源。重点检查aws_instance.web_server的ami值是否是一个有效的ami-开头的IDaws_iam_role.ssm_role的name是否包含dev后缀aws_iam_instance_profile.ssm_profile是否被创建user_data是否非空表示SSM安装逻辑已启用。4.3.3 应用部署让代码变为现实# 执行部署 terraform apply -var-fileterraform-dev.tfvars -auto-approve # -auto-approve 跳过交互式确认适合CI/CD。首次手动执行可去掉此参数看到确认提示后再输入yes部署过程约需2-3分钟。成功后你会看到类似输出

相关新闻

最新新闻

日新闻

周新闻

月新闻