I have a Terraform module to deploy instances on AWS whenever a coworker needs one. For each person I deploy :
- An ec2 instance
- An elastic IP
- A private security group
In order to centralize everything I have made the organisation of the module like so :
.
├── README.md
├── coworker1
│ ├── main.tf
│ └── variables.tf
├── keys
├── coworker2
│ ├── main.tf
│ └── variables.tf
├── templates
│ ├── cloud-init.tpl
│ ├── pubkey_rsa.pub
│ └── strat.json
├── terraform.tf
├── terraform.tfstate
└── terraform.tfstate.backup
I use the teraform.tf file in the root module and use a module block to reference submodules.
module "corworker1" {
source = "./coworker1"
}
I run terraform apply from the root module and its ressources are correctly created.
However when I run another terraform apply it says that it will need to recreate the instance and modify the elastic ip because of changes. Even when I did not change anything.
# module.coworker1.aws_instance.ec2_instance must be replaced
-/+ resource "aws_instance" "ec2_instance" {
~ arn = "arn:aws:ec2:eu-central-1:XXXXX:instance/i-0f498ac75ad66199e" -> (known after apply)
~ associate_public_ip_address = true -> (known after apply)
~ cpu_core_count = 1 -> (known after apply)
~ cpu_threads_per_core = 1 -> (known after apply)
- disable_api_termination = false -> null
- ebs_optimized = false -> null
- hibernation = false -> null
+ host_id = (known after apply)
~ id = "i-0f498ac75ad66199e" -> (known after apply)
~ instance_initiated_shutdown_behavior = "stop" -> (known after apply)
~ instance_state = "running" -> (known after apply)
~ ipv6_address_count = 0 -> (known after apply)
~ ipv6_addresses = [] -> (known after apply)
+ key_name = (known after apply)
- monitoring = false -> null
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
~ primary_network_interface_id = "eni-06debade3df83944b" -> (known after apply)
~ private_dns = "ip-172-31-12-126.eu-central-1.compute.internal" -> (known after apply)
~ private_ip = "172.31.12.126" -> (known after apply)
~ public_dns = "ec2-3-67-119-191.eu-central-1.compute.amazonaws.com" -> (known after apply)
~ public_ip = "3.67.119.191" -> (known after apply)
~ secondary_private_ips = [] -> (known after apply)
~ security_groups = [
- "ec2 coworker1.example.com",
] -> (known after apply)
~ subnet_id = "subnet-3e48ca72" -> (known after apply)
tags = {
"HiddenName" = "coworker1"
"Name" = "coworker1.example.com"
"Operator" = "coworker1@example.com"
}
~ tenancy = "default" -> (known after apply)
# (9 unchanged attributes hidden)
~ capacity_reservation_specification {
~ capacity_reservation_preference = "open" -> (known after apply)
+ capacity_reservation_target {
+ capacity_reservation_id = (known after apply)
}
}
- credit_specification {
- cpu_credits = "standard" -> null
}
+ ebs_block_device { # forces replacement
+ delete_on_termination = true
+ device_name = "/dev/sda1"
+ encrypted = (known after apply)
+ iops = (known after apply)
+ kms_key_id = (known after apply)
+ snapshot_id = (known after apply)
+ throughput = (known after apply)
+ volume_id = (known after apply)
+ volume_size = 200
+ volume_type = (known after apply)
}
- ebs_block_device { # forces replacement
- delete_on_termination = true -> null
- device_name = "/dev/sda1" -> null
- encrypted = false -> null
- iops = 600 -> null
- snapshot_id = "snap-0d8c6e92a59dec0c9" -> null
- tags = {} -> null
- throughput = 0 -> null
- volume_id = "vol-007f3d2d27b510740" -> null
- volume_size = 200 -> null
- volume_type = "gp2" -> null
}
~ enclave_options {
~ enabled = false -> (known after apply)
}
+ ephemeral_block_device {
+ device_name = (known after apply)
+ no_device = (known after apply)
+ virtual_name = (known after apply)
}
~ metadata_options {
~ http_endpoint = "enabled" -> (known after apply)
~ http_put_response_hop_limit = 1 -> (known after apply)
~ http_tokens = "optional" -> (known after apply)
}
+ network_interface {
+ delete_on_termination = (known after apply)
+ device_index = (known after apply)
+ network_interface_id = (known after apply)
}
~ root_block_device {
~ delete_on_termination = true -> (known after apply)
~ device_name = "/dev/sda1" -> (known after apply)
~ encrypted = false -> (known after apply)
~ iops = 600 -> (known after apply)
+ kms_key_id = (known after apply)
~ tags = {} -> (known after apply)
~ throughput = 0 -> (known after apply)
~ volume_id = "vol-007f3d2d27b510740" -> (known after apply)
~ volume_size = 200 -> (known after apply)
~ volume_type = "gp2" -> (known after apply)
}
}
This is the file of a submodule
main.tf
data "aws_ami" "ubuntu" {
owners = ["amazon"]
filter {
name = "name"
values = ["Deep Learning Base AMI (Ubuntu 18.04) Version 38.0"]
}
}
resource "aws_eip" "lb" {
instance = aws_instance.ec2_instance.id
vpc = true
}
resource "aws_security_group" "instance_private_group" {
description = "Private security group for the ${var.username}.example.com instance"
name = "ec2 ${var.username}.example.com"
vpc_id = data.aws_vpc.debian-ec2-env.id
ingress {
description = "description"
protocol = "TCP"
from_port = 22
to_port = 22
cidr_blocks = ["XX.XX.XX.XX/32"]
}
}
data "aws_vpc" "debian-ec2-env" {
id = var.vpc_id
}
data "aws_iam_instance_profile" "instance_profile" {
name = var.instance_profile_name
}
resource "aws_instance" "ec2_instance" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
vpc_security_group_ids = [aws_security_group.instance_private_group.id]
availability_zone = var.az
iam_instance_profile = data.aws_iam_instance_profile.instance_profile.name
user_data = templatefile("${path.root}/templates/cloud-init.tpl", { username = var.username, admins = var.admins })
ebs_block_device {
device_name = "/dev/sda1"
volume_size = var.storage_size
}
tags = {
Name = "${var.username}.example.com"
HiddenName = var.username
Operator = "${var.username}@example.com"
}
}
What do you think is the cause of this ?
EDIT 1 :
I tried to re run apply after tweaking some logging in order to reduce the amount of logging preventing me to see an important detail. The user data does'nt appear to have changed. BUT I now see this :
Note: Objects have changed outside of Terraform
Terraform detected the following changes made outside of Terraform since the last "terraform apply":
# module.coworker1.aws_eip.lb has been changed
~ resource "aws_eip" "lb" {
id = "eipalloc-52f1ee6c"
+ tags = {}
# (12 unchanged attributes hidden)
}
# module.coworker1.aws_instance.ec2_instance has been changed
~ resource "aws_instance" "ec2_instance" {
id = "i-0f498ac75ad66199e"
~ public_dns = "ec2-18-197-141-73.eu-central-1.compute.amazonaws.com" -> "ec2-3-67-119-191.eu-central-1.compute.amazonaws.com"
~ public_ip = "18.197.141.73" -> "3.67.119.191"
tags = {
"HiddenName" = "coworker1"
"Name" = "coworker1.example.com"
"Operator" = "coworker1@example.com"
}
# (28 unchanged attributes hidden)
# (6 unchanged blocks hidden)
}
So if I understand correctly the public IP is changed after the Elastic IP is created and it is causing diff from the results that Terraform displayed. Should I place a dependance on the Elastic IP on the ec2 instance ?