skip to Main Content

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


  1. You can use the scp command to copy an updated shell script to your EC2 instance without recreating it every time. For example:

    scp /path/to/your/script.sh ec2-user@your-instance-ip:/path/on/ec2/
    

    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.

    Login or Signup to reply.
  2. associate_public_ip_address attribute is forcing your ec2 recreation, and it is changing because your subnet might have map_public_ip_on_launch to true and hence it is setting this attribute to all ec2 instances launched in this subnet.

    Once you set associate_public_ip_address to true you won’t experience the same behavior.

    resource "aws_instance" "my-ec2" {
      ami           = data.aws_ami.ubuntu.id
      instance_type = "t3.micro" # was 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 = true ## <-- This was the probelem Earlier false --> ##
    }
    

    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 to Yes.

    You can also check it via terraform by adding output and doing terraform plan , considering the subnet is already created.

    output "subnet_option_map_public_ip_on_launch" {
      value = aws_subnet.public_subnet_a.map_public_ip_on_launch
    }
    

    You will get below response

    + subnet_option_map_public_ip_on_launch = true
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search