使用Terraform创建AWS EC2实例时上传脚本失败,请求协助
解决Terraform创建EC2时上传脚本与配置Cronjob的问题
我完全理解你作为Terraform新手遇到这种卡壳的情况有多闹心——刚入门时我也在文件上传和初始化脚本上踩过不少坑!咱们一步步拆解问题,给你几个靠谱的解决方案:
方案1:修复File Provisioner的配置(最直接的文件上传方式)
你之前用File Provisioner失败,大概率是缺少SSH连接配置——这个Provisioner需要通过SSH连接到EC2实例才能传输文件,默认不会自动帮你建立连接。下面是完整的配置示例:
resource "aws_instance" "my_ec2" { ami = "ami-xxxxxx" # 替换成你所在区域的Amazon Linux 2 AMI instance_type = "t2.micro" key_name = "your-key-pair-name" # 替换成你的密钥对名称 # 安全组必须开放SSH端口(22),否则无法连接 vpc_security_group_ids = [aws_security_group.allow_ssh.id] # File Provisioner:上传本地脚本到EC2实例 provisioner "file" { source = "abc.sh" # 本地脚本路径 destination = "/home/ec2-user/basic2.sh" # 实例上的目标路径 # 关键:添加SSH连接配置 connection { type = "ssh" user = "ec2-user" # Amazon Linux 2的默认用户名,Ubuntu是ubuntu private_key = file("~/.ssh/your-private-key.pem") # 本地私钥路径 host = self.public_ip # 自动获取实例的公网IP } } # 可选:用remote-exec给脚本加执行权限 provisioner "remote-exec" { inline = [ "chmod +x /home/ec2-user/basic2.sh" ] connection { type = "ssh" user = "ec2-user" private_key = file("~/.ssh/your-private-key.pem") host = self.public_ip } } } # 配套的安全组:允许SSH访问 resource "aws_security_group" "allow_ssh" { name = "allow-ssh" description = "Allow SSH inbound traffic" ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] # 生产环境建议限制成你的IP,比如"192.168.1.1/32" } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
注意事项:
- 确保你的私钥文件权限正确:
chmod 400 ~/.ssh/your-private-key.pem,否则SSH会拒绝连接 - 如果实例在私有子网,需要通过堡垒机或者SSM Session Manager连接,这时可以把connection换成SSM类型(需要实例有SSM权限)
方案2:用User Data + Templatefile(更可靠的初始化方式)
User Data是EC2实例启动时自动执行的脚本,不需要额外的SSH连接,适合做初始化操作。你之前用的template_file是旧的数据源,现在Terraform推荐用内置的templatefile函数,更简洁:
步骤1:创建本地脚本模板(比如user_data.tpl)
#!/bin/bash # 创建目录并写入脚本内容 mkdir -p /home/ec2-user/files2 cat > /home/ec2-user/files2/basic2.sh << 'EOF' # 这里复制你的abc.sh的内容,比如: echo "脚本执行时间:$(date)" >> /home/ec2-user/script_run.log EOF # 给脚本加执行权限 chmod +x /home/ec2-user/files2/basic2.sh # 配置Cronjob:每天凌晨1点执行脚本,输出日志到系统日志 echo "0 1 * * * ec2-user /home/ec2-user/files2/basic2.sh >> /var/log/basic_script.log 2>&1" | sudo tee /etc/cron.d/basic_cron
步骤2:在Terraform中引用这个模板
resource "aws_instance" "my_ec2" { ami = "ami-xxxxxx" instance_type = "t2.micro" key_name = "your-key-pair-name" vpc_security_group_ids = [aws_security_group.basic.id] # 使用templatefile加载用户数据模板 user_data = templatefile("${path.module}/user_data.tpl", {}) } # 配套安全组:允许必要的出站流量(比如下载依赖) resource "aws_security_group" "basic" { name = "basic-security-group" description = "Allow outbound traffic" egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
这种方法的好处是:实例启动时自动完成所有配置,不需要等待SSH连接,也不会因为网络问题导致上传失败。
方案3:用SSM Parameter Store存储脚本(适合敏感或复杂脚本)
如果你的脚本包含敏感信息,或者需要在多个实例复用,可以把脚本存在SSM Parameter Store里,然后在User Data中拉取:
#!/bin/bash # 从SSM拉取脚本内容并写入文件 aws ssm get-parameter --name "/my/scripts/basic.sh" --with-decryption --query "Parameter.Value" --output text > /home/ec2-user/basic2.sh chmod +x /home/ec2-user/basic2.sh
注意:实例需要有ssm:GetParameter的IAM权限,所以要给实例附加对应的IAM角色。
常见问题排查
- File Provisioner连接失败:检查安全组是否开22端口,私钥路径和权限是否正确,实例公网IP是否能访问
- User Data没执行:查看实例的
/var/log/cloud-init-output.log日志,里面会有执行细节和错误信息 - Cronjob不生效:检查
/var/log/cron日志,确保Cron服务在运行(sudo systemctl status crond),脚本路径和权限正确
内容的提问来源于stack exchange,提问作者pratik




