I am trying to copy the shell script to the running ec2 every time the shell scripts get updated.
The main requirement is I do not want the running EC2 to be stopped recreated or rebuilt, but Terraform should just copy the updated shell script from local to inside the running EC2 instance.
Below is my code using null_resource and triggers based on the script file’s sha256 changes. But it destroys the EC2 and Elastic IP and recreates fresh ones every time when I try to run terraform apply
.
Kindly help.
resource "aws_instance" "my-ec2" {
ami = var.ami
instance_type = "t3.medium"
root_block_device {
volume_size = 20
}
key_name = aws_key_pair.server_instance.key_name
vpc_security_group_ids = [aws_security_group.server_sg.id]
subnet_id = aws_subnet.public_subnet_a.id
associate_public_ip_address = false
}
resource "aws_eip" "server_eip" {
domain = "vpc"
}
resource "aws_eip_association" "eip-association" {
instance_id = aws_instance.my-ec2.id
allocation_id = aws_eip.server_eip_eip.id
}
resource "null_resource" "shell_script" {
triggers = {
script_sha = sha256(file("${path.module}/script.sh"))
}
provisioner "file" {
source = "./script.sh"
destination = "/home/ubuntu/script.sh"
}
connection {
type = "ssh"
user = "ubuntu"
private_key = tls_private_key.server_instance.private_key_openssh
host = aws_eip.server_eip.public_ip
}
}
The terraform plan output shows the following, it says it will do the replacement of Elastic IP -> EC2 along with shell script update. I don’t get why.
Terraform used the selected providers to generate the following execution plan. Resource
actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_eip_association.eip-association must be replaced
-/+ resource "aws_eip_association" "eip-association" {
~ id = "eipassoc-0c2d1f2b1622c8944" -> (known after apply)
~ instance_id = "i-0a40c91f920bd27e0" # forces replacement -> (known after apply) # forces replacement
~ network_interface_id = "eni-07fc89a7a961ca3ff" -> (known after apply)
~ private_ip_address = "172.16.0.231" -> (known after apply)
~ public_ip = "54.#.#.#" -> (known after apply)
# (1 unchanged attribute hidden)
}
# aws_instance.my-ec2 must be replaced
-/+ resource "aws_instance" "my-ec2" {
~ arn = "arn:aws:ec2:eu-west-1:575725418355:instance/i-0a40c91f920bd27e0" -> (known after apply)
~ associate_public_ip_address = true -> false # forces replacement
~ availability_zone = "eu-west-1a" -> (known after apply)
~ cpu_core_count = 1 -> (known after apply)
~ cpu_threads_per_core = 2 -> (known after apply)
~ disable_api_stop = false -> (known after apply)
~ disable_api_termination = false -> (known after apply)
~ ebs_optimized = false -> (known after apply)
- hibernation = false -> null
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
~ id = "i-0a40c91f920bd27e0" -> (known after apply)
~ instance_initiated_shutdown_behavior = "stop" -> (known after apply)
+ instance_lifecycle = (known after apply)
~ instance_state = "running" -> (known after apply)
~ ipv6_address_count = 0 -> (known after apply)
~ ipv6_addresses = [] -> (known after apply)
~ monitoring = false -> (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
~ placement_partition_number = 0 -> (known after apply)
~ primary_network_interface_id = "eni-07fc89a7a961ca3ff" -> (known after apply)
~ private_dns = "ip-172-16-0-231.eu-west-1.compute.internal" -> (known after apply)
~ private_ip = "172.16.0.231" -> (known after apply)
+ public_dns = (known after apply)
~ public_ip = "54.#.#.#" -> (known after apply)
~ secondary_private_ips = [] -> (known after apply)
~ security_groups = [] -> (known after apply)
+ spot_instance_request_id = (known after apply)
}
# null_resource.shell_script must be replaced
-/+ resource "null_resource" "shell_script" {
~ id = "7785759108528064923" -> (known after apply)
~ triggers = { # forces replacement
~ "script_sha" = "d3d741441a86d7928d58ea9596bd2b57d8556953e81064d937136301759f8fe9" -> "1fb47cc144844c24a25b92ca1b03147b50daa0ab49f016eb6637de9845404639"
}
}
Plan: 3 to add, 0 to change, 3 to destroy.
Changes to Outputs:
~ Instance_id = "i-0a40c91f920bd27e0" -> (known after apply)
2
Answers
You can use the
scp
command to copy an updated shell script to your EC2 instance without recreating it every time. For example:Replace
/path/to/your/script.sh
with the local path to your updated script,ec2-user
with the appropriate username for your EC2 instance,your-instance-ip
with the public IP address of your EC2 instance, and/path/on/ec2/
with the directory path on the EC2 instance where you want to copy the script. This command will copy the updated script to your EC2 instance without recreating it.associate_public_ip_address
attribute is forcing your ec2 recreation, and it is changing because your subnet might havemap_public_ip_on_launch
totrue
and hence it is setting this attribute to all ec2 instances launched in this subnet.Once you set
associate_public_ip_address
totrue
you won’t experience the same behavior.Please check your subnet on the AWS Console in which you are deploying this EC2 Instance, it must have the
Auto-assign public IPv4 address
toYes
.You can also check it via terraform by adding output and doing
terraform plan
, considering the subnet is already created.You will get below response