diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 465e790..be35845 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -180,6 +180,48 @@ module "ec2_ignore_ami_changes" { tags = local.tags } +################################################################################ +# EC2 Module - with ignore AMI changes and ignore user_data changes +################################################################################ + +module "ec2_ignore_ami_user_data_changes" { + source = "../../" + + name = local.name + + ignore_ami_changes = true + ignore_user_data_changes = true + + ami = data.aws_ami.amazon_linux.id + instance_type = "t2.micro" + availability_zone = element(module.vpc.azs, 0) + subnet_id = element(module.vpc.private_subnets, 0) + vpc_security_group_ids = [module.security_group.security_group_id] + + tags = local.tags +} + +################################################################################ +# EC2 Module - with ignore user_data changes +################################################################################ + +module "ec2_ignore_user_data_changes" { + source = "../../" + + name = local.name + + ignore_user_data_changes = true + + ami = data.aws_ami.amazon_linux.id + instance_type = "t2.micro" + availability_zone = element(module.vpc.azs, 0) + subnet_id = element(module.vpc.private_subnets, 0) + vpc_security_group_ids = [module.security_group.security_group_id] + + tags = local.tags +} + + ################################################################################ # EC2 Module - multiple instances with `for_each` ################################################################################ diff --git a/main.tf b/main.tf index 749e67d..f46dd9b 100644 --- a/main.tf +++ b/main.tf @@ -19,7 +19,7 @@ data "aws_ssm_parameter" "this" { ################################################################################ resource "aws_instance" "this" { - count = local.create && !var.ignore_ami_changes && !var.create_spot_instance ? 1 : 0 + count = local.create && !var.ignore_ami_changes && !var.ignore_user_data_changes && !var.create_spot_instance ? 1 : 0 ami = local.ami instance_type = var.instance_type @@ -197,7 +197,7 @@ resource "aws_instance" "this" { ################################################################################ resource "aws_instance" "ignore_ami" { - count = local.create && var.ignore_ami_changes && !var.create_spot_instance ? 1 : 0 + count = local.create && var.ignore_ami_changes && !var.ignore_user_data_changes && !var.create_spot_instance ? 1 : 0 ami = local.ami instance_type = var.instance_type @@ -376,6 +376,373 @@ resource "aws_instance" "ignore_ami" { } } +################################################################################ +# Instance - Ignore UserData Changes +################################################################################ + +resource "aws_instance" "ignore_user_data" { + count = local.create && !var.ignore_ami_changes && var.ignore_user_data_changes && !var.create_spot_instance ? 1 : 0 + + ami = local.ami + instance_type = var.instance_type + cpu_core_count = var.cpu_core_count + cpu_threads_per_core = var.cpu_threads_per_core + hibernation = var.hibernation + + user_data = var.user_data + user_data_base64 = var.user_data_base64 + user_data_replace_on_change = var.user_data_replace_on_change + + availability_zone = var.availability_zone + subnet_id = var.subnet_id + vpc_security_group_ids = var.vpc_security_group_ids + + key_name = var.key_name + monitoring = var.monitoring + get_password_data = var.get_password_data + iam_instance_profile = var.create_iam_instance_profile ? aws_iam_instance_profile.this[0].name : var.iam_instance_profile + + associate_public_ip_address = var.associate_public_ip_address + private_ip = var.private_ip + secondary_private_ips = var.secondary_private_ips + ipv6_address_count = var.ipv6_address_count + ipv6_addresses = var.ipv6_addresses + + ebs_optimized = var.ebs_optimized + + dynamic "cpu_options" { + for_each = length(var.cpu_options) > 0 ? [var.cpu_options] : [] + + content { + core_count = try(cpu_options.value.core_count, null) + threads_per_core = try(cpu_options.value.threads_per_core, null) + amd_sev_snp = try(cpu_options.value.amd_sev_snp, null) + } + } + + dynamic "capacity_reservation_specification" { + for_each = length(var.capacity_reservation_specification) > 0 ? [var.capacity_reservation_specification] : [] + + content { + capacity_reservation_preference = try(capacity_reservation_specification.value.capacity_reservation_preference, null) + + dynamic "capacity_reservation_target" { + for_each = try([capacity_reservation_specification.value.capacity_reservation_target], []) + + content { + capacity_reservation_id = try(capacity_reservation_target.value.capacity_reservation_id, null) + capacity_reservation_resource_group_arn = try(capacity_reservation_target.value.capacity_reservation_resource_group_arn, null) + } + } + } + } + + dynamic "root_block_device" { + for_each = var.root_block_device + + content { + delete_on_termination = try(root_block_device.value.delete_on_termination, null) + encrypted = try(root_block_device.value.encrypted, null) + iops = try(root_block_device.value.iops, null) + kms_key_id = lookup(root_block_device.value, "kms_key_id", null) + volume_size = try(root_block_device.value.volume_size, null) + volume_type = try(root_block_device.value.volume_type, null) + throughput = try(root_block_device.value.throughput, null) + tags = try(root_block_device.value.tags, null) + } + } + + dynamic "ebs_block_device" { + for_each = var.ebs_block_device + + content { + delete_on_termination = try(ebs_block_device.value.delete_on_termination, null) + device_name = ebs_block_device.value.device_name + encrypted = try(ebs_block_device.value.encrypted, null) + iops = try(ebs_block_device.value.iops, null) + kms_key_id = lookup(ebs_block_device.value, "kms_key_id", null) + snapshot_id = lookup(ebs_block_device.value, "snapshot_id", null) + volume_size = try(ebs_block_device.value.volume_size, null) + volume_type = try(ebs_block_device.value.volume_type, null) + throughput = try(ebs_block_device.value.throughput, null) + tags = try(ebs_block_device.value.tags, null) + } + } + + dynamic "ephemeral_block_device" { + for_each = var.ephemeral_block_device + + content { + device_name = ephemeral_block_device.value.device_name + no_device = try(ephemeral_block_device.value.no_device, null) + virtual_name = try(ephemeral_block_device.value.virtual_name, null) + } + } + + dynamic "metadata_options" { + for_each = length(var.metadata_options) > 0 ? [var.metadata_options] : [] + + content { + http_endpoint = try(metadata_options.value.http_endpoint, "enabled") + http_tokens = try(metadata_options.value.http_tokens, "optional") + http_put_response_hop_limit = try(metadata_options.value.http_put_response_hop_limit, 1) + instance_metadata_tags = try(metadata_options.value.instance_metadata_tags, null) + } + } + + dynamic "network_interface" { + for_each = var.network_interface + + content { + device_index = network_interface.value.device_index + network_interface_id = lookup(network_interface.value, "network_interface_id", null) + delete_on_termination = try(network_interface.value.delete_on_termination, false) + } + } + + dynamic "private_dns_name_options" { + for_each = length(var.private_dns_name_options) > 0 ? [var.private_dns_name_options] : [] + + content { + hostname_type = try(private_dns_name_options.value.hostname_type, null) + enable_resource_name_dns_a_record = try(private_dns_name_options.value.enable_resource_name_dns_a_record, null) + enable_resource_name_dns_aaaa_record = try(private_dns_name_options.value.enable_resource_name_dns_aaaa_record, null) + } + } + + dynamic "launch_template" { + for_each = length(var.launch_template) > 0 ? [var.launch_template] : [] + + content { + id = lookup(var.launch_template, "id", null) + name = lookup(var.launch_template, "name", null) + version = lookup(var.launch_template, "version", null) + } + } + + dynamic "maintenance_options" { + for_each = length(var.maintenance_options) > 0 ? [var.maintenance_options] : [] + + content { + auto_recovery = try(maintenance_options.value.auto_recovery, null) + } + } + + enclave_options { + enabled = var.enclave_options_enabled + } + + source_dest_check = length(var.network_interface) > 0 ? null : var.source_dest_check + disable_api_termination = var.disable_api_termination + disable_api_stop = var.disable_api_stop + instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior + placement_group = var.placement_group + tenancy = var.tenancy + host_id = var.host_id + + credit_specification { + cpu_credits = local.is_t_instance_type ? var.cpu_credits : null + } + + timeouts { + create = try(var.timeouts.create, null) + update = try(var.timeouts.update, null) + delete = try(var.timeouts.delete, null) + } + + tags = merge({ "Name" = var.name }, var.instance_tags, var.tags) + volume_tags = var.enable_volume_tags ? merge({ "Name" = var.name }, var.volume_tags) : null + + lifecycle { + ignore_changes = [ + user_data + ] + } +} + + +################################################################################ +# Instance - Ignore AMI Changes and user_data Changes +################################################################################ + +resource "aws_instance" "ignore_ami_userdata" { + count = local.create && var.ignore_ami_changes && var.ignore_user_data_changes && !var.create_spot_instance ? 1 : 0 + + ami = local.ami + instance_type = var.instance_type + cpu_core_count = var.cpu_core_count + cpu_threads_per_core = var.cpu_threads_per_core + hibernation = var.hibernation + + user_data = var.user_data + user_data_base64 = var.user_data_base64 + user_data_replace_on_change = var.user_data_replace_on_change + + availability_zone = var.availability_zone + subnet_id = var.subnet_id + vpc_security_group_ids = var.vpc_security_group_ids + + key_name = var.key_name + monitoring = var.monitoring + get_password_data = var.get_password_data + iam_instance_profile = var.create_iam_instance_profile ? aws_iam_instance_profile.this[0].name : var.iam_instance_profile + + associate_public_ip_address = var.associate_public_ip_address + private_ip = var.private_ip + secondary_private_ips = var.secondary_private_ips + ipv6_address_count = var.ipv6_address_count + ipv6_addresses = var.ipv6_addresses + + ebs_optimized = var.ebs_optimized + + dynamic "cpu_options" { + for_each = length(var.cpu_options) > 0 ? [var.cpu_options] : [] + + content { + core_count = try(cpu_options.value.core_count, null) + threads_per_core = try(cpu_options.value.threads_per_core, null) + amd_sev_snp = try(cpu_options.value.amd_sev_snp, null) + } + } + + dynamic "capacity_reservation_specification" { + for_each = length(var.capacity_reservation_specification) > 0 ? [var.capacity_reservation_specification] : [] + + content { + capacity_reservation_preference = try(capacity_reservation_specification.value.capacity_reservation_preference, null) + + dynamic "capacity_reservation_target" { + for_each = try([capacity_reservation_specification.value.capacity_reservation_target], []) + + content { + capacity_reservation_id = try(capacity_reservation_target.value.capacity_reservation_id, null) + capacity_reservation_resource_group_arn = try(capacity_reservation_target.value.capacity_reservation_resource_group_arn, null) + } + } + } + } + + dynamic "root_block_device" { + for_each = var.root_block_device + + content { + delete_on_termination = try(root_block_device.value.delete_on_termination, null) + encrypted = try(root_block_device.value.encrypted, null) + iops = try(root_block_device.value.iops, null) + kms_key_id = lookup(root_block_device.value, "kms_key_id", null) + volume_size = try(root_block_device.value.volume_size, null) + volume_type = try(root_block_device.value.volume_type, null) + throughput = try(root_block_device.value.throughput, null) + tags = try(root_block_device.value.tags, null) + } + } + + dynamic "ebs_block_device" { + for_each = var.ebs_block_device + + content { + delete_on_termination = try(ebs_block_device.value.delete_on_termination, null) + device_name = ebs_block_device.value.device_name + encrypted = try(ebs_block_device.value.encrypted, null) + iops = try(ebs_block_device.value.iops, null) + kms_key_id = lookup(ebs_block_device.value, "kms_key_id", null) + snapshot_id = lookup(ebs_block_device.value, "snapshot_id", null) + volume_size = try(ebs_block_device.value.volume_size, null) + volume_type = try(ebs_block_device.value.volume_type, null) + throughput = try(ebs_block_device.value.throughput, null) + tags = try(ebs_block_device.value.tags, null) + } + } + + dynamic "ephemeral_block_device" { + for_each = var.ephemeral_block_device + + content { + device_name = ephemeral_block_device.value.device_name + no_device = try(ephemeral_block_device.value.no_device, null) + virtual_name = try(ephemeral_block_device.value.virtual_name, null) + } + } + + dynamic "metadata_options" { + for_each = length(var.metadata_options) > 0 ? [var.metadata_options] : [] + + content { + http_endpoint = try(metadata_options.value.http_endpoint, "enabled") + http_tokens = try(metadata_options.value.http_tokens, "optional") + http_put_response_hop_limit = try(metadata_options.value.http_put_response_hop_limit, 1) + instance_metadata_tags = try(metadata_options.value.instance_metadata_tags, null) + } + } + + dynamic "network_interface" { + for_each = var.network_interface + + content { + device_index = network_interface.value.device_index + network_interface_id = lookup(network_interface.value, "network_interface_id", null) + delete_on_termination = try(network_interface.value.delete_on_termination, false) + } + } + + dynamic "private_dns_name_options" { + for_each = length(var.private_dns_name_options) > 0 ? [var.private_dns_name_options] : [] + + content { + hostname_type = try(private_dns_name_options.value.hostname_type, null) + enable_resource_name_dns_a_record = try(private_dns_name_options.value.enable_resource_name_dns_a_record, null) + enable_resource_name_dns_aaaa_record = try(private_dns_name_options.value.enable_resource_name_dns_aaaa_record, null) + } + } + + dynamic "launch_template" { + for_each = length(var.launch_template) > 0 ? [var.launch_template] : [] + + content { + id = lookup(var.launch_template, "id", null) + name = lookup(var.launch_template, "name", null) + version = lookup(var.launch_template, "version", null) + } + } + + dynamic "maintenance_options" { + for_each = length(var.maintenance_options) > 0 ? [var.maintenance_options] : [] + + content { + auto_recovery = try(maintenance_options.value.auto_recovery, null) + } + } + + enclave_options { + enabled = var.enclave_options_enabled + } + + source_dest_check = length(var.network_interface) > 0 ? null : var.source_dest_check + disable_api_termination = var.disable_api_termination + disable_api_stop = var.disable_api_stop + instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior + placement_group = var.placement_group + tenancy = var.tenancy + host_id = var.host_id + + credit_specification { + cpu_credits = local.is_t_instance_type ? var.cpu_credits : null + } + + timeouts { + create = try(var.timeouts.create, null) + update = try(var.timeouts.update, null) + delete = try(var.timeouts.delete, null) + } + + tags = merge({ "Name" = var.name }, var.instance_tags, var.tags) + volume_tags = var.enable_volume_tags ? merge({ "Name" = var.name }, var.volume_tags) : null + + lifecycle { + ignore_changes = [ami, user_data] + } +} + ################################################################################ # Spot Instance ################################################################################ diff --git a/variables.tf b/variables.tf index 38a1b5b..404cc87 100644 --- a/variables.tf +++ b/variables.tf @@ -28,6 +28,12 @@ variable "ignore_ami_changes" { default = false } +variable "ignore_user_data_changes" { + description = "Whether changes to the userdata should be ignored by Terraform. Note - changing this value will result in the replacement of the instance" + type = bool + default = false +} + variable "associate_public_ip_address" { description = "Whether to associate a public IP address with an instance in a VPC" type = bool