From 9d4e0ca389535522808ddf988e3a6510bb143161 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 28 Apr 2023 16:58:19 -0400 Subject: [PATCH] feat!: Raise minimum required Terraform version to 1.0+ (#331) --- .pre-commit-config.yaml | 2 +- README.md | 46 ++-- examples/README.md | 8 + examples/complete/README.md | 9 +- examples/complete/main.tf | 40 ++-- examples/complete/versions.tf | 4 +- examples/volume-attachment/README.md | 9 +- examples/volume-attachment/main.tf | 91 ++++---- examples/volume-attachment/versions.tf | 4 +- main.tf | 284 ++++++++++++++++++++----- outputs.tf | 112 ++++++++-- variables.tf | 38 ++-- versions.tf | 4 +- wrappers/main.tf | 85 ++++---- wrappers/outputs.tf | 2 +- 15 files changed, 511 insertions(+), 227 deletions(-) create mode 100644 examples/README.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 75deea30..e940bf7d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.77.0 + rev: v1.77.3 hooks: - id: terraform_fmt - id: terraform_wrapper_module_for_each diff --git a/README.md b/README.md index 29f28a94..33d8151c 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,9 @@ Terraform module which creates an EC2 instance on AWS. ```hcl module "ec2_instance" { source = "terraform-aws-modules/ec2-instance/aws" - version = "~> 3.0" name = "single-instance" - ami = "ami-ebd02392" instance_type = "t2.micro" key_name = "user1" monitoring = true @@ -34,13 +32,11 @@ module "ec2_instance" { ```hcl module "ec2_instance" { source = "terraform-aws-modules/ec2-instance/aws" - version = "~> 3.0" for_each = toset(["one", "two", "three"]) name = "instance-${each.key}" - ami = "ami-ebd02392" instance_type = "t2.micro" key_name = "user1" monitoring = true @@ -59,7 +55,6 @@ module "ec2_instance" { ```hcl module "ec2_instance" { source = "terraform-aws-modules/ec2-instance/aws" - version = "~> 3.0" name = "spot-instance" @@ -67,7 +62,6 @@ module "ec2_instance" { spot_price = "0.60" spot_type = "persistent" - ami = "ami-ebd02392" instance_type = "t2.micro" key_name = "user1" monitoring = true @@ -167,14 +161,14 @@ The following combinations are supported to conditionally create resources: | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.1 | -| [aws](#requirement\_aws) | >= 4.20.0 | +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.20 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.20.0 | +| [aws](#provider\_aws) | >= 4.20 | ## Modules @@ -187,6 +181,7 @@ No modules. | [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_instance.ignore_ami](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | | [aws_instance.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | | [aws_spot_instance_request.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/spot_instance_request) | resource | | [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | @@ -202,20 +197,20 @@ No modules. | [associate\_public\_ip\_address](#input\_associate\_public\_ip\_address) | Whether to associate a public IP address with an instance in a VPC | `bool` | `null` | no | | [availability\_zone](#input\_availability\_zone) | AZ to start the instance in | `string` | `null` | no | | [capacity\_reservation\_specification](#input\_capacity\_reservation\_specification) | Describes an instance's Capacity Reservation targeting option | `any` | `{}` | no | -| [cpu\_core\_count](#input\_cpu\_core\_count) | Sets the number of CPU cores for an instance. | `number` | `null` | no | +| [cpu\_core\_count](#input\_cpu\_core\_count) | Sets the number of CPU cores for an instance | `number` | `null` | no | | [cpu\_credits](#input\_cpu\_credits) | The credit option for CPU usage (unlimited or standard) | `string` | `null` | no | -| [cpu\_threads\_per\_core](#input\_cpu\_threads\_per\_core) | Sets the number of CPU threads per core for an instance (has no effect unless cpu\_core\_count is also set). | `number` | `null` | no | +| [cpu\_threads\_per\_core](#input\_cpu\_threads\_per\_core) | Sets the number of CPU threads per core for an instance (has no effect unless cpu\_core\_count is also set) | `number` | `null` | no | | [create](#input\_create) | Whether to create an instance | `bool` | `true` | no | | [create\_iam\_instance\_profile](#input\_create\_iam\_instance\_profile) | Determines whether an IAM instance profile is created or to use an existing IAM instance profile | `bool` | `false` | no | | [create\_spot\_instance](#input\_create\_spot\_instance) | Depicts if the instance is a spot instance | `bool` | `false` | no | -| [disable\_api\_stop](#input\_disable\_api\_stop) | If true, enables EC2 Instance Stop Protection. | `bool` | `null` | no | +| [disable\_api\_stop](#input\_disable\_api\_stop) | If true, enables EC2 Instance Stop Protection | `bool` | `null` | no | | [disable\_api\_termination](#input\_disable\_api\_termination) | If true, enables EC2 Instance Termination Protection | `bool` | `null` | no | | [ebs\_block\_device](#input\_ebs\_block\_device) | Additional EBS block devices to attach to the instance | `list(any)` | `[]` | no | | [ebs\_optimized](#input\_ebs\_optimized) | If true, the launched EC2 instance will be EBS-optimized | `bool` | `null` | no | | [enable\_volume\_tags](#input\_enable\_volume\_tags) | Whether to enable volume tags (if enabled it conflicts with root\_block\_device tags) | `bool` | `true` | no | | [enclave\_options\_enabled](#input\_enclave\_options\_enabled) | Whether Nitro Enclaves will be enabled on the instance. Defaults to `false` | `bool` | `null` | no | | [ephemeral\_block\_device](#input\_ephemeral\_block\_device) | Customize Ephemeral (also known as Instance Store) volumes on the instance | `list(map(string))` | `[]` | no | -| [get\_password\_data](#input\_get\_password\_data) | If true, wait for password data to become available and retrieve it. | `bool` | `null` | no | +| [get\_password\_data](#input\_get\_password\_data) | If true, wait for password data to become available and retrieve it | `bool` | `null` | no | | [hibernation](#input\_hibernation) | If true, the launched EC2 instance will support hibernation | `bool` | `null` | no | | [host\_id](#input\_host\_id) | ID of a dedicated host that the instance will be assigned to. Use when an instance is to be launched on a specific dedicated host | `string` | `null` | no | | [iam\_instance\_profile](#input\_iam\_instance\_profile) | IAM Instance Profile to launch the instance with. Specified as the name of the Instance Profile | `string` | `null` | no | @@ -226,15 +221,16 @@ No modules. | [iam\_role\_policies](#input\_iam\_role\_policies) | Policies attached to the IAM role | `map(string)` | `{}` | no | | [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role/profile created | `map(string)` | `{}` | no | | [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name` or `name`) is used as a prefix | `bool` | `true` | no | +| [ignore\_ami\_changes](#input\_ignore\_ami\_changes) | Whether changes to the AMI ID changes should be ignored by Terraform. Note - changing this value will result in the replacement of the instance | `bool` | `false` | no | | [instance\_initiated\_shutdown\_behavior](#input\_instance\_initiated\_shutdown\_behavior) | Shutdown behavior for the instance. Amazon defaults this to stop for EBS-backed instances and terminate for instance-store instances. Cannot be set on instance-store instance | `string` | `null` | no | | [instance\_type](#input\_instance\_type) | The type of instance to start | `string` | `"t3.micro"` | no | | [ipv6\_address\_count](#input\_ipv6\_address\_count) | A number of IPv6 addresses to associate with the primary network interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet | `number` | `null` | no | | [ipv6\_addresses](#input\_ipv6\_addresses) | Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface | `list(string)` | `null` | no | | [key\_name](#input\_key\_name) | Key name of the Key Pair to use for the instance; which can be managed using the `aws_key_pair` resource | `string` | `null` | no | -| [launch\_template](#input\_launch\_template) | Specifies a Launch Template to configure the instance. Parameters configured on this resource will override the corresponding parameters in the Launch Template | `map(string)` | `null` | no | +| [launch\_template](#input\_launch\_template) | Specifies a Launch Template to configure the instance. Parameters configured on this resource will override the corresponding parameters in the Launch Template | `map(string)` | `{}` | no | | [maintenance\_options](#input\_maintenance\_options) | The maintenance options for the instance | `any` | `{}` | no | -| [metadata\_options](#input\_metadata\_options) | Customize the metadata options of the instance | `map(string)` | `{}` | no | -| [monitoring](#input\_monitoring) | If true, the launched EC2 instance will have detailed monitoring enabled | `bool` | `false` | no | +| [metadata\_options](#input\_metadata\_options) | Customize the metadata options of the instance | `map(string)` |
{
"http_endpoint": "enabled",
"http_put_response_hop_limit": 1,
"http_tokens": "optional"
}
| no | +| [monitoring](#input\_monitoring) | If true, the launched EC2 instance will have detailed monitoring enabled | `bool` | `null` | no | | [name](#input\_name) | Name to be used on EC2 instance created | `string` | `""` | no | | [network\_interface](#input\_network\_interface) | Customize network interfaces to be attached at instance boot time | `list(map(string))` | `[]` | no | | [placement\_group](#input\_placement\_group) | The Placement Group to start the instance in | `string` | `null` | no | @@ -242,7 +238,7 @@ No modules. | [putin\_khuylo](#input\_putin\_khuylo) | Do you agree that Putin doesn't respect Ukrainian sovereignty and territorial integrity? More info: https://en.wikipedia.org/wiki/Putin_khuylo! | `bool` | `true` | no | | [root\_block\_device](#input\_root\_block\_device) | Customize details about the root block device of the instance. See Block Devices below for details | `list(any)` | `[]` | no | | [secondary\_private\_ips](#input\_secondary\_private\_ips) | A list of secondary private IPv4 addresses to assign to the instance's primary network interface (eth0) in a VPC. Can only be assigned to the primary network interface (eth0) attached at instance creation, not a pre-existing network interface i.e. referenced in a `network_interface block` | `list(string)` | `null` | no | -| [source\_dest\_check](#input\_source\_dest\_check) | Controls if traffic is routed to the instance when the destination address does not match the instance. Used for NAT or VPNs. | `bool` | `true` | no | +| [source\_dest\_check](#input\_source\_dest\_check) | Controls if traffic is routed to the instance when the destination address does not match the instance. Used for NAT or VPNs | `bool` | `null` | no | | [spot\_block\_duration\_minutes](#input\_spot\_block\_duration\_minutes) | The required duration for the Spot instances, in minutes. This value must be a multiple of 60 (60, 120, 180, 240, 300, or 360) | `number` | `null` | no | | [spot\_instance\_interruption\_behavior](#input\_spot\_instance\_interruption\_behavior) | Indicates Spot instance behavior when it is interrupted. Valid values are `terminate`, `stop`, or `hibernate` | `string` | `null` | no | | [spot\_launch\_group](#input\_spot\_launch\_group) | A launch group is a group of spot instances that launch together and terminate together. If left empty instances are launched and terminated individually | `string` | `null` | no | @@ -253,11 +249,11 @@ No modules. | [spot\_wait\_for\_fulfillment](#input\_spot\_wait\_for\_fulfillment) | If set, Terraform will wait for the Spot Request to be fulfilled, and will throw an error if the timeout of 10m is reached | `bool` | `null` | no | | [subnet\_id](#input\_subnet\_id) | The VPC Subnet ID to launch in | `string` | `null` | no | | [tags](#input\_tags) | A mapping of tags to assign to the resource | `map(string)` | `{}` | no | -| [tenancy](#input\_tenancy) | The tenancy of the instance (if the instance is running in a VPC). Available values: default, dedicated, host. | `string` | `null` | no | +| [tenancy](#input\_tenancy) | The tenancy of the instance (if the instance is running in a VPC). Available values: default, dedicated, host | `string` | `null` | no | | [timeouts](#input\_timeouts) | Define maximum timeout for creating, updating, and deleting EC2 instance resources | `map(string)` | `{}` | no | -| [user\_data](#input\_user\_data) | The user data to provide when launching the instance. Do not pass gzip-compressed data via this argument; see user\_data\_base64 instead. | `string` | `null` | no | -| [user\_data\_base64](#input\_user\_data\_base64) | Can be used instead of user\_data to pass base64-encoded binary data directly. Use this instead of user\_data whenever the value is not a valid UTF-8 string. For example, gzip-encoded user data must be base64-encoded and passed via this argument to avoid corruption. | `string` | `null` | no | -| [user\_data\_replace\_on\_change](#input\_user\_data\_replace\_on\_change) | When used in combination with user\_data or user\_data\_base64 will trigger a destroy and recreate when set to true. Defaults to false if not set. | `bool` | `false` | no | +| [user\_data](#input\_user\_data) | The user data to provide when launching the instance. Do not pass gzip-compressed data via this argument; see user\_data\_base64 instead | `string` | `null` | no | +| [user\_data\_base64](#input\_user\_data\_base64) | Can be used instead of user\_data to pass base64-encoded binary data directly. Use this instead of user\_data whenever the value is not a valid UTF-8 string. For example, gzip-encoded user data must be base64-encoded and passed via this argument to avoid corruption | `string` | `null` | no | +| [user\_data\_replace\_on\_change](#input\_user\_data\_replace\_on\_change) | When used in combination with user\_data or user\_data\_base64 will trigger a destroy and recreate when set to true. Defaults to false if not set | `bool` | `null` | no | | [volume\_tags](#input\_volume\_tags) | A mapping of tags to assign to the devices created by the instance at launch time | `map(string)` | `{}` | no | | [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | A list of security group IDs to associate with | `list(string)` | `null` | no | @@ -265,7 +261,7 @@ No modules. | Name | Description | |------|-------------| -| [ami](#output\_ami) | AMI ID that was used to create the instance. | +| [ami](#output\_ami) | AMI ID that was used to create the instance | | [arn](#output\_arn) | The ARN of the instance | | [capacity\_reservation\_specification](#output\_capacity\_reservation\_specification) | Capacity reservation specification of the instance | | [ebs\_block\_device](#output\_ebs\_block\_device) | EBS block device information | @@ -277,13 +273,13 @@ No modules. | [iam\_role\_name](#output\_iam\_role\_name) | The name of the IAM role | | [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [id](#output\_id) | The ID of the instance | -| [instance\_state](#output\_instance\_state) | The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped` | -| [ipv6\_addresses](#output\_ipv6\_addresses) | The IPv6 address assigned to the instance, if applicable. | +| [instance\_state](#output\_instance\_state) | The state of the instance | +| [ipv6\_addresses](#output\_ipv6\_addresses) | The IPv6 address assigned to the instance, if applicable | | [outpost\_arn](#output\_outpost\_arn) | The ARN of the Outpost the instance is assigned to | | [password\_data](#output\_password\_data) | Base-64 encoded encrypted password data for the instance. Useful for getting the administrator password for instances running Microsoft Windows. This attribute is only exported if `get_password_data` is true | | [primary\_network\_interface\_id](#output\_primary\_network\_interface\_id) | The ID of the instance's primary network interface | | [private\_dns](#output\_private\_dns) | The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC | -| [private\_ip](#output\_private\_ip) | The private IP address assigned to the instance. | +| [private\_ip](#output\_private\_ip) | The private IP address assigned to the instance | | [public\_dns](#output\_public\_dns) | The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC | | [public\_ip](#output\_public\_ip) | The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws\_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached | | [root\_block\_device](#output\_root\_block\_device) | Root block device information | diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..f417c0ad --- /dev/null +++ b/examples/README.md @@ -0,0 +1,8 @@ +# Examples + +Please note - the examples provided serve two primary means: + +1. Show users working examples of the various ways in which the module can be configured and features supported +2. A means of testing/validating module changes + +Please do not mistake the examples provided as "best practices". It is up to users to consult the AWS service documentation for best practices, usage recommendations, etc. diff --git a/examples/complete/README.md b/examples/complete/README.md index 380f3bdc..63accd79 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -19,14 +19,14 @@ Note that this example may create resources which can cost money. Run `terraform | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.1 | -| [aws](#requirement\_aws) | >= 4.7 | +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.20 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.7 | +| [aws](#provider\_aws) | >= 4.20 | ## Modules @@ -43,7 +43,7 @@ Note that this example may create resources which can cost money. Run `terraform | [ec2\_t3\_unlimited](#module\_ec2\_t3\_unlimited) | ../../ | n/a | | [ec2\_targeted\_capacity\_reservation](#module\_ec2\_targeted\_capacity\_reservation) | ../../ | n/a | | [security\_group](#module\_security\_group) | terraform-aws-modules/security-group/aws | ~> 4.0 | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 4.0 | ## Resources @@ -55,6 +55,7 @@ Note that this example may create resources which can cost money. Run `terraform | [aws_network_interface.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_interface) | resource | | [aws_placement_group.web](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/placement_group) | resource | | [aws_ami.amazon_linux](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | ## Inputs diff --git a/examples/complete/main.tf b/examples/complete/main.tf index c19992a3..4d181936 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -2,18 +2,24 @@ provider "aws" { region = local.region } +data "aws_availability_zones" "available" {} + locals { - name = "example-ec2-complete" + name = "ex-${basename(path.cwd)}" region = "eu-west-1" + vpc_cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + user_data = <<-EOT - #!/bin/bash - echo "Hello Terraform!" + #!/bin/bash + echo "Hello Terraform!" EOT tags = { - Owner = "user" - Environment = "dev" + Name = local.name + Example = local.name + Repository = "https://github.com/terraform-aws-modules/terraform-aws-ec2-instance" } } @@ -21,12 +27,6 @@ locals { # EC2 Module ################################################################################ -module "ec2_disabled" { - source = "../../" - - create = false -} - module "ec2_complete" { source = "../../" @@ -153,6 +153,13 @@ module "ec2_t3_unlimited" { tags = local.tags } + +module "ec2_disabled" { + source = "../../" + + create = false +} + ################################################################################ # EC2 Module - multiple instances with `for_each` ################################################################################ @@ -333,15 +340,14 @@ resource "aws_ec2_capacity_reservation" "targeted" { module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "~> 3.0" + version = "~> 4.0" name = local.name - cidr = "10.99.0.0/18" + cidr = local.vpc_cidr - azs = ["${local.region}a", "${local.region}b", "${local.region}c"] - public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] - private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] - database_subnets = ["10.99.7.0/24", "10.99.8.0/24", "10.99.9.0/24"] + azs = local.azs + private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] + public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] tags = local.tags } diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf index 36060f73..eddf9d5b 100644 --- a/examples/complete/versions.tf +++ b/examples/complete/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 0.13.1" + required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.7" + version = ">= 4.20" } } } diff --git a/examples/volume-attachment/README.md b/examples/volume-attachment/README.md index af4ffc6e..adede89a 100644 --- a/examples/volume-attachment/README.md +++ b/examples/volume-attachment/README.md @@ -21,14 +21,14 @@ Note that this example may create resources which can cost money. Run `terraform | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.1 | -| [aws](#requirement\_aws) | >= 3.72 | +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.20 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 3.72 | +| [aws](#provider\_aws) | >= 4.20 | ## Modules @@ -36,7 +36,7 @@ Note that this example may create resources which can cost money. Run `terraform |------|--------|---------| | [ec2](#module\_ec2) | ../../ | n/a | | [security\_group](#module\_security\_group) | terraform-aws-modules/security-group/aws | ~> 4.0 | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 4.0 | ## Resources @@ -45,6 +45,7 @@ Note that this example may create resources which can cost money. Run `terraform | [aws_ebs_volume.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ebs_volume) | resource | | [aws_volume_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/volume_attachment) | resource | | [aws_ami.amazon_linux](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | ## Inputs diff --git a/examples/volume-attachment/main.tf b/examples/volume-attachment/main.tf index 526773fe..f201cad2 100644 --- a/examples/volume-attachment/main.tf +++ b/examples/volume-attachment/main.tf @@ -2,31 +2,68 @@ provider "aws" { region = local.region } +data "aws_availability_zones" "available" {} + locals { - availability_zone = "${local.region}a" - name = "example-ec2-volume-attachment" - region = "eu-west-1" + name = "ex-${basename(path.cwd)}" + region = "eu-west-1" + + vpc_cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + tags = { - Owner = "user" - Environment = "dev" + Name = local.name + Example = local.name + Repository = "https://github.com/terraform-aws-modules/terraform-aws-ec2-instance" } } +################################################################################ +# EC2 Module +################################################################################ + +module "ec2" { + source = "../../" + + name = local.name + + ami = data.aws_ami.amazon_linux.id + instance_type = "c5.large" + availability_zone = element(local.azs, 0) + subnet_id = element(module.vpc.private_subnets, 0) + vpc_security_group_ids = [module.security_group.security_group_id] + associate_public_ip_address = true + + tags = local.tags +} + +resource "aws_volume_attachment" "this" { + device_name = "/dev/sdh" + volume_id = aws_ebs_volume.this.id + instance_id = module.ec2.id +} + +resource "aws_ebs_volume" "this" { + availability_zone = element(local.azs, 0) + size = 1 + + tags = local.tags +} + ################################################################################ # Supporting Resources ################################################################################ module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "~> 3.0" + version = "~> 4.0" name = local.name - cidr = "10.99.0.0/18" + cidr = local.vpc_cidr - azs = ["${local.region}a", "${local.region}b", "${local.region}c"] - public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] - private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] - database_subnets = ["10.99.7.0/24", "10.99.8.0/24", "10.99.9.0/24"] + azs = local.azs + private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] + public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] tags = local.tags } @@ -55,35 +92,3 @@ module "security_group" { tags = local.tags } - -################################################################################ -# EC2 Module -################################################################################ - -module "ec2" { - source = "../../" - - name = local.name - - ami = data.aws_ami.amazon_linux.id - instance_type = "c5.large" - availability_zone = local.availability_zone - subnet_id = element(module.vpc.private_subnets, 0) - vpc_security_group_ids = [module.security_group.security_group_id] - associate_public_ip_address = true - - tags = local.tags -} - -resource "aws_volume_attachment" "this" { - device_name = "/dev/sdh" - volume_id = aws_ebs_volume.this.id - instance_id = module.ec2.id -} - -resource "aws_ebs_volume" "this" { - availability_zone = local.availability_zone - size = 1 - - tags = local.tags -} diff --git a/examples/volume-attachment/versions.tf b/examples/volume-attachment/versions.tf index 22e8d726..eddf9d5b 100644 --- a/examples/volume-attachment/versions.tf +++ b/examples/volume-attachment/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 0.13.1" + required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" - version = ">= 3.72" + version = ">= 4.20" } } } diff --git a/main.tf b/main.tf index 75faea7a..46b23b46 100644 --- a/main.tf +++ b/main.tf @@ -3,7 +3,7 @@ data "aws_partition" "current" {} locals { create = var.create && var.putin_khuylo - is_t_instance_type = replace(var.instance_type, "/^t(2|3|3a){1}\\..*$/", "1") == "1" ? true : false + is_t_instance_type = replace(var.instance_type, "/^t(2|3|3a|4g){1}\\..*$/", "1") == "1" ? true : false } data "aws_ssm_parameter" "this" { @@ -17,7 +17,7 @@ data "aws_ssm_parameter" "this" { ################################################################################ resource "aws_instance" "this" { - count = local.create && !var.create_spot_instance ? 1 : 0 + count = local.create && !var.ignore_ami_changes && !var.create_spot_instance ? 1 : 0 ami = try(coalesce(var.ami, nonsensitive(data.aws_ssm_parameter.this[0].value)), null) instance_type = var.instance_type @@ -48,11 +48,171 @@ resource "aws_instance" "this" { 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 "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.tags) + volume_tags = var.enable_volume_tags ? merge({ "Name" = var.name }, var.volume_tags) : null +} + +################################################################################ +# Instance - Ignore AMI Changes +################################################################################ + +resource "aws_instance" "ignore_ami" { + count = local.create && var.ignore_ami_changes && !var.create_spot_instance ? 1 : 0 + + ami = try(coalesce(var.ami, nonsensitive(data.aws_ssm_parameter.this[0].value)), null) + 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 "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) @@ -63,64 +223,70 @@ resource "aws_instance" "this" { dynamic "root_block_device" { for_each = var.root_block_device + content { - delete_on_termination = lookup(root_block_device.value, "delete_on_termination", null) - encrypted = lookup(root_block_device.value, "encrypted", null) - iops = lookup(root_block_device.value, "iops", null) + 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 = lookup(root_block_device.value, "volume_size", null) - volume_type = lookup(root_block_device.value, "volume_type", null) - throughput = lookup(root_block_device.value, "throughput", null) - tags = lookup(root_block_device.value, "tags", 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 = lookup(ebs_block_device.value, "delete_on_termination", null) + delete_on_termination = try(ebs_block_device.value.delete_on_termination, null) device_name = ebs_block_device.value.device_name - encrypted = lookup(ebs_block_device.value, "encrypted", null) - iops = lookup(ebs_block_device.value, "iops", null) + 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 = lookup(ebs_block_device.value, "volume_size", null) - volume_type = lookup(ebs_block_device.value, "volume_type", null) - throughput = lookup(ebs_block_device.value, "throughput", null) - tags = lookup(ebs_block_device.value, "tags", 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 = lookup(ephemeral_block_device.value, "no_device", null) - virtual_name = lookup(ephemeral_block_device.value, "virtual_name", null) + 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 = var.metadata_options != null ? [var.metadata_options] : [] + for_each = length(var.metadata_options) > 0 ? [var.metadata_options] : [] + content { - http_endpoint = lookup(metadata_options.value, "http_endpoint", "enabled") - http_tokens = lookup(metadata_options.value, "http_tokens", "optional") - http_put_response_hop_limit = lookup(metadata_options.value, "http_put_response_hop_limit", "1") - instance_metadata_tags = lookup(metadata_options.value, "instance_metadata_tags", null) + 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 = lookup(network_interface.value, "delete_on_termination", false) + delete_on_termination = try(network_interface.value.delete_on_termination, false) } } dynamic "launch_template" { - for_each = var.launch_template != null ? [var.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) @@ -130,6 +296,7 @@ resource "aws_instance" "this" { dynamic "maintenance_options" { for_each = length(var.maintenance_options) > 0 ? [var.maintenance_options] : [] + content { auto_recovery = try(maintenance_options.value.auto_recovery, null) } @@ -152,13 +319,19 @@ resource "aws_instance" "this" { } timeouts { - create = lookup(var.timeouts, "create", null) - update = lookup(var.timeouts, "update", null) - delete = lookup(var.timeouts, "delete", null) + create = try(var.timeouts.create, null) + update = try(var.timeouts.update, null) + delete = try(var.timeouts.delete, null) } tags = merge({ "Name" = var.name }, var.tags) volume_tags = var.enable_volume_tags ? merge({ "Name" = var.name }, var.volume_tags) : null + + lifecycle { + ignore_changes = [ + ami + ] + } } ################################################################################ @@ -208,6 +381,7 @@ resource "aws_spot_instance_request" "this" { 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) @@ -223,62 +397,70 @@ resource "aws_spot_instance_request" "this" { dynamic "root_block_device" { for_each = var.root_block_device + content { - delete_on_termination = lookup(root_block_device.value, "delete_on_termination", null) - encrypted = lookup(root_block_device.value, "encrypted", null) - iops = lookup(root_block_device.value, "iops", null) + 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 = lookup(root_block_device.value, "volume_size", null) - volume_type = lookup(root_block_device.value, "volume_type", null) - throughput = lookup(root_block_device.value, "throughput", null) - tags = lookup(root_block_device.value, "tags", 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 = lookup(ebs_block_device.value, "delete_on_termination", null) + delete_on_termination = try(ebs_block_device.value.delete_on_termination, null) device_name = ebs_block_device.value.device_name - encrypted = lookup(ebs_block_device.value, "encrypted", null) - iops = lookup(ebs_block_device.value, "iops", null) + 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 = lookup(ebs_block_device.value, "volume_size", null) - volume_type = lookup(ebs_block_device.value, "volume_type", null) - throughput = lookup(ebs_block_device.value, "throughput", 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 = lookup(ephemeral_block_device.value, "no_device", null) - virtual_name = lookup(ephemeral_block_device.value, "virtual_name", null) + 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 = var.metadata_options != null ? [var.metadata_options] : [] + for_each = length(var.metadata_options) > 0 ? [var.metadata_options] : [] + content { - http_endpoint = lookup(metadata_options.value, "http_endpoint", "enabled") - http_tokens = lookup(metadata_options.value, "http_tokens", "optional") - http_put_response_hop_limit = lookup(metadata_options.value, "http_put_response_hop_limit", "1") + 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 = lookup(network_interface.value, "delete_on_termination", false) + delete_on_termination = try(network_interface.value.delete_on_termination, false) } } dynamic "launch_template" { - for_each = var.launch_template != null ? [var.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) @@ -302,8 +484,8 @@ resource "aws_spot_instance_request" "this" { } timeouts { - create = lookup(var.timeouts, "create", null) - delete = lookup(var.timeouts, "delete", null) + create = try(var.timeouts.create, null) + delete = try(var.timeouts.delete, null) } tags = merge({ "Name" = var.name }, var.tags) diff --git a/outputs.tf b/outputs.tf index 1e5c90aa..04610766 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,86 +1,156 @@ output "id" { description = "The ID of the instance" - value = try(aws_instance.this[0].id, aws_spot_instance_request.this[0].id, "") + value = try( + aws_instance.this[0].id, + aws_instance.ignore_ami[0].id, + aws_spot_instance_request.this[0].id, + null, + ) } output "arn" { description = "The ARN of the instance" - value = try(aws_instance.this[0].arn, aws_spot_instance_request.this[0].arn, "") + value = try( + aws_instance.this[0].arn, + aws_instance.ignore_ami[0].arn, + aws_spot_instance_request.this[0].arn, + null, + ) } output "capacity_reservation_specification" { description = "Capacity reservation specification of the instance" - value = try(aws_instance.this[0].capacity_reservation_specification, aws_spot_instance_request.this[0].capacity_reservation_specification, "") + value = try( + aws_instance.this[0].capacity_reservation_specification, + aws_instance.ignore_ami[0].capacity_reservation_specification, + aws_spot_instance_request.this[0].capacity_reservation_specification, + null, + ) } output "instance_state" { - description = "The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped`" - value = try(aws_instance.this[0].instance_state, aws_spot_instance_request.this[0].instance_state, "") + description = "The state of the instance" + value = try( + aws_instance.this[0].instance_state, + aws_instance.ignore_ami[0].instance_state, + aws_spot_instance_request.this[0].instance_state, + null, + ) } output "outpost_arn" { description = "The ARN of the Outpost the instance is assigned to" - value = try(aws_instance.this[0].outpost_arn, aws_spot_instance_request.this[0].outpost_arn, "") + value = try( + aws_instance.this[0].outpost_arn, + aws_instance.ignore_ami[0].outpost_arn, + aws_spot_instance_request.this[0].outpost_arn, + null, + ) } output "password_data" { description = "Base-64 encoded encrypted password data for the instance. Useful for getting the administrator password for instances running Microsoft Windows. This attribute is only exported if `get_password_data` is true" - value = try(aws_instance.this[0].password_data, aws_spot_instance_request.this[0].password_data, "") + value = try( + aws_instance.this[0].password_data, + aws_instance.ignore_ami[0].password_data, + aws_spot_instance_request.this[0].password_data, + null, + ) } output "primary_network_interface_id" { description = "The ID of the instance's primary network interface" - value = try(aws_instance.this[0].primary_network_interface_id, aws_spot_instance_request.this[0].primary_network_interface_id, "") + value = try( + aws_instance.this[0].primary_network_interface_id, + aws_instance.ignore_ami[0].primary_network_interface_id, + aws_spot_instance_request.this[0].primary_network_interface_id, + null, + ) } output "private_dns" { description = "The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC" - value = try(aws_instance.this[0].private_dns, aws_spot_instance_request.this[0].private_dns, "") + value = try( + aws_instance.this[0].private_dns, + aws_instance.ignore_ami[0].private_dns, + aws_spot_instance_request.this[0].private_dns, + null, + ) } output "public_dns" { description = "The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC" - value = try(aws_instance.this[0].public_dns, aws_spot_instance_request.this[0].public_dns, "") + value = try( + aws_instance.this[0].public_dns, + aws_instance.ignore_ami[0].public_dns, + aws_spot_instance_request.this[0].public_dns, + null, + ) } output "public_ip" { description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached" - value = try(aws_instance.this[0].public_ip, aws_spot_instance_request.this[0].public_ip, "") + value = try( + aws_instance.this[0].public_ip, + aws_instance.ignore_ami[0].public_ip, + aws_spot_instance_request.this[0].public_ip, + null, + ) } output "private_ip" { - description = "The private IP address assigned to the instance." - value = try(aws_instance.this[0].private_ip, aws_spot_instance_request.this[0].private_ip, "") + description = "The private IP address assigned to the instance" + value = try( + aws_instance.this[0].private_ip, + aws_instance.ignore_ami[0].private_ip, + aws_spot_instance_request.this[0].private_ip, + null, + ) } output "ipv6_addresses" { - description = "The IPv6 address assigned to the instance, if applicable." - value = try(aws_instance.this[0].ipv6_addresses, []) + description = "The IPv6 address assigned to the instance, if applicable" + value = try( + aws_instance.this[0].ipv6_addresses, + aws_instance.ignore_ami[0].ipv6_addresses, + aws_spot_instance_request.this[0].ipv6_addresses, + [], + ) } output "tags_all" { description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block" - value = try(aws_instance.this[0].tags_all, aws_spot_instance_request.this[0].tags_all, {}) + value = try( + aws_instance.this[0].tags_all, + aws_instance.ignore_ami[0].tags_all, + aws_spot_instance_request.this[0].tags_all, + {}, + ) } output "spot_bid_status" { description = "The current bid status of the Spot Instance Request" - value = try(aws_spot_instance_request.this[0].spot_bid_status, "") + value = try(aws_spot_instance_request.this[0].spot_bid_status, null) } output "spot_request_state" { description = "The current request state of the Spot Instance Request" - value = try(aws_spot_instance_request.this[0].spot_request_state, "") + value = try(aws_spot_instance_request.this[0].spot_request_state, null) } output "spot_instance_id" { description = "The Instance ID (if any) that is currently fulfilling the Spot Instance request" - value = try(aws_spot_instance_request.this[0].spot_instance_id, "") + value = try(aws_spot_instance_request.this[0].spot_instance_id, null) } output "ami" { - description = "AMI ID that was used to create the instance." - value = try(aws_instance.this[0].ami, aws_spot_instance_request.this[0].ami, "") + description = "AMI ID that was used to create the instance" + value = try( + aws_instance.this[0].ami, + aws_instance.ignore_ami[0].ami, + aws_spot_instance_request.this[0].ami, + null, + ) } ################################################################################ diff --git a/variables.tf b/variables.tf index bc8d1595..b1b92dbf 100644 --- a/variables.tf +++ b/variables.tf @@ -22,6 +22,12 @@ variable "ami" { default = null } +variable "ignore_ami_changes" { + description = "Whether changes to the AMI ID changes 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 @@ -83,7 +89,7 @@ variable "ephemeral_block_device" { } variable "get_password_data" { - description = "If true, wait for password data to become available and retrieve it." + description = "If true, wait for password data to become available and retrieve it" type = bool default = null } @@ -139,19 +145,23 @@ variable "key_name" { variable "launch_template" { description = "Specifies a Launch Template to configure the instance. Parameters configured on this resource will override the corresponding parameters in the Launch Template" type = map(string) - default = null + default = {} } variable "metadata_options" { description = "Customize the metadata options of the instance" type = map(string) - default = {} + default = { + "http_endpoint" = "enabled" + "http_put_response_hop_limit" = 1 + "http_tokens" = "optional" + } } variable "monitoring" { description = "If true, the launched EC2 instance will have detailed monitoring enabled" type = bool - default = false + default = null } variable "network_interface" { @@ -185,9 +195,9 @@ variable "secondary_private_ips" { } variable "source_dest_check" { - description = "Controls if traffic is routed to the instance when the destination address does not match the instance. Used for NAT or VPNs." + description = "Controls if traffic is routed to the instance when the destination address does not match the instance. Used for NAT or VPNs" type = bool - default = true + default = null } variable "subnet_id" { @@ -203,27 +213,27 @@ variable "tags" { } variable "tenancy" { - description = "The tenancy of the instance (if the instance is running in a VPC). Available values: default, dedicated, host." + description = "The tenancy of the instance (if the instance is running in a VPC). Available values: default, dedicated, host" type = string default = null } variable "user_data" { - description = "The user data to provide when launching the instance. Do not pass gzip-compressed data via this argument; see user_data_base64 instead." + description = "The user data to provide when launching the instance. Do not pass gzip-compressed data via this argument; see user_data_base64 instead" type = string default = null } variable "user_data_base64" { - description = "Can be used instead of user_data to pass base64-encoded binary data directly. Use this instead of user_data whenever the value is not a valid UTF-8 string. For example, gzip-encoded user data must be base64-encoded and passed via this argument to avoid corruption." + description = "Can be used instead of user_data to pass base64-encoded binary data directly. Use this instead of user_data whenever the value is not a valid UTF-8 string. For example, gzip-encoded user data must be base64-encoded and passed via this argument to avoid corruption" type = string default = null } variable "user_data_replace_on_change" { - description = "When used in combination with user_data or user_data_base64 will trigger a destroy and recreate when set to true. Defaults to false if not set." + description = "When used in combination with user_data or user_data_base64 will trigger a destroy and recreate when set to true. Defaults to false if not set" type = bool - default = false + default = null } variable "volume_tags" { @@ -251,13 +261,13 @@ variable "timeouts" { } variable "cpu_core_count" { - description = "Sets the number of CPU cores for an instance." # This option is only supported on creation of instance type that support CPU Options https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-optimize-cpu.html#cpu-options-supported-instances-values + description = "Sets the number of CPU cores for an instance" # This option is only supported on creation of instance type that support CPU Options https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-optimize-cpu.html#cpu-options-supported-instances-values type = number default = null } variable "cpu_threads_per_core" { - description = "Sets the number of CPU threads per core for an instance (has no effect unless cpu_core_count is also set)." + description = "Sets the number of CPU threads per core for an instance (has no effect unless cpu_core_count is also set)" type = number default = null } @@ -318,7 +328,7 @@ variable "spot_valid_from" { } variable "disable_api_stop" { - description = "If true, enables EC2 Instance Stop Protection." + description = "If true, enables EC2 Instance Stop Protection" type = bool default = null diff --git a/versions.tf b/versions.tf index 0836352d..eddf9d5b 100644 --- a/versions.tf +++ b/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 0.13.1" + required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.20.0" + version = ">= 4.20" } } } diff --git a/wrappers/main.tf b/wrappers/main.tf index c9700f24..12a4b59a 100644 --- a/wrappers/main.tf +++ b/wrappers/main.tf @@ -7,6 +7,7 @@ module "wrapper" { name = try(each.value.name, var.defaults.name, "") ami_ssm_parameter = try(each.value.ami_ssm_parameter, var.defaults.ami_ssm_parameter, "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2") ami = try(each.value.ami, var.defaults.ami, null) + ignore_ami_changes = try(each.value.ignore_ami_changes, var.defaults.ignore_ami_changes, false) associate_public_ip_address = try(each.value.associate_public_ip_address, var.defaults.associate_public_ip_address, null) maintenance_options = try(each.value.maintenance_options, var.defaults.maintenance_options, {}) availability_zone = try(each.value.availability_zone, var.defaults.availability_zone, null) @@ -26,44 +27,48 @@ module "wrapper" { ipv6_address_count = try(each.value.ipv6_address_count, var.defaults.ipv6_address_count, null) ipv6_addresses = try(each.value.ipv6_addresses, var.defaults.ipv6_addresses, null) key_name = try(each.value.key_name, var.defaults.key_name, null) - launch_template = try(each.value.launch_template, var.defaults.launch_template, null) - metadata_options = try(each.value.metadata_options, var.defaults.metadata_options, {}) - monitoring = try(each.value.monitoring, var.defaults.monitoring, false) - network_interface = try(each.value.network_interface, var.defaults.network_interface, []) - placement_group = try(each.value.placement_group, var.defaults.placement_group, null) - private_ip = try(each.value.private_ip, var.defaults.private_ip, null) - root_block_device = try(each.value.root_block_device, var.defaults.root_block_device, []) - secondary_private_ips = try(each.value.secondary_private_ips, var.defaults.secondary_private_ips, null) - source_dest_check = try(each.value.source_dest_check, var.defaults.source_dest_check, true) - subnet_id = try(each.value.subnet_id, var.defaults.subnet_id, null) - tags = try(each.value.tags, var.defaults.tags, {}) - tenancy = try(each.value.tenancy, var.defaults.tenancy, null) - user_data = try(each.value.user_data, var.defaults.user_data, null) - user_data_base64 = try(each.value.user_data_base64, var.defaults.user_data_base64, null) - user_data_replace_on_change = try(each.value.user_data_replace_on_change, var.defaults.user_data_replace_on_change, false) - volume_tags = try(each.value.volume_tags, var.defaults.volume_tags, {}) - enable_volume_tags = try(each.value.enable_volume_tags, var.defaults.enable_volume_tags, true) - vpc_security_group_ids = try(each.value.vpc_security_group_ids, var.defaults.vpc_security_group_ids, null) - timeouts = try(each.value.timeouts, var.defaults.timeouts, {}) - cpu_core_count = try(each.value.cpu_core_count, var.defaults.cpu_core_count, null) - cpu_threads_per_core = try(each.value.cpu_threads_per_core, var.defaults.cpu_threads_per_core, null) - create_spot_instance = try(each.value.create_spot_instance, var.defaults.create_spot_instance, false) - spot_price = try(each.value.spot_price, var.defaults.spot_price, null) - spot_wait_for_fulfillment = try(each.value.spot_wait_for_fulfillment, var.defaults.spot_wait_for_fulfillment, null) - spot_type = try(each.value.spot_type, var.defaults.spot_type, null) - spot_launch_group = try(each.value.spot_launch_group, var.defaults.spot_launch_group, null) - spot_block_duration_minutes = try(each.value.spot_block_duration_minutes, var.defaults.spot_block_duration_minutes, null) - spot_instance_interruption_behavior = try(each.value.spot_instance_interruption_behavior, var.defaults.spot_instance_interruption_behavior, null) - spot_valid_until = try(each.value.spot_valid_until, var.defaults.spot_valid_until, null) - spot_valid_from = try(each.value.spot_valid_from, var.defaults.spot_valid_from, null) - disable_api_stop = try(each.value.disable_api_stop, var.defaults.disable_api_stop, null) - putin_khuylo = try(each.value.putin_khuylo, var.defaults.putin_khuylo, true) - create_iam_instance_profile = try(each.value.create_iam_instance_profile, var.defaults.create_iam_instance_profile, false) - iam_role_name = try(each.value.iam_role_name, var.defaults.iam_role_name, null) - iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, var.defaults.iam_role_use_name_prefix, true) - iam_role_path = try(each.value.iam_role_path, var.defaults.iam_role_path, null) - iam_role_description = try(each.value.iam_role_description, var.defaults.iam_role_description, null) - iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, var.defaults.iam_role_permissions_boundary, null) - iam_role_policies = try(each.value.iam_role_policies, var.defaults.iam_role_policies, {}) - iam_role_tags = try(each.value.iam_role_tags, var.defaults.iam_role_tags, {}) + launch_template = try(each.value.launch_template, var.defaults.launch_template, {}) + metadata_options = try(each.value.metadata_options, var.defaults.metadata_options, { + "http_endpoint" = "enabled" + "http_put_response_hop_limit" = 1 + "http_tokens" = "optional" + }) + monitoring = try(each.value.monitoring, var.defaults.monitoring, null) + network_interface = try(each.value.network_interface, var.defaults.network_interface, []) + placement_group = try(each.value.placement_group, var.defaults.placement_group, null) + private_ip = try(each.value.private_ip, var.defaults.private_ip, null) + root_block_device = try(each.value.root_block_device, var.defaults.root_block_device, []) + secondary_private_ips = try(each.value.secondary_private_ips, var.defaults.secondary_private_ips, null) + source_dest_check = try(each.value.source_dest_check, var.defaults.source_dest_check, null) + subnet_id = try(each.value.subnet_id, var.defaults.subnet_id, null) + tags = try(each.value.tags, var.defaults.tags, {}) + tenancy = try(each.value.tenancy, var.defaults.tenancy, null) + user_data = try(each.value.user_data, var.defaults.user_data, null) + user_data_base64 = try(each.value.user_data_base64, var.defaults.user_data_base64, null) + user_data_replace_on_change = try(each.value.user_data_replace_on_change, var.defaults.user_data_replace_on_change, null) + volume_tags = try(each.value.volume_tags, var.defaults.volume_tags, {}) + enable_volume_tags = try(each.value.enable_volume_tags, var.defaults.enable_volume_tags, true) + vpc_security_group_ids = try(each.value.vpc_security_group_ids, var.defaults.vpc_security_group_ids, null) + timeouts = try(each.value.timeouts, var.defaults.timeouts, {}) + cpu_core_count = try(each.value.cpu_core_count, var.defaults.cpu_core_count, null) + cpu_threads_per_core = try(each.value.cpu_threads_per_core, var.defaults.cpu_threads_per_core, null) + create_spot_instance = try(each.value.create_spot_instance, var.defaults.create_spot_instance, false) + spot_price = try(each.value.spot_price, var.defaults.spot_price, null) + spot_wait_for_fulfillment = try(each.value.spot_wait_for_fulfillment, var.defaults.spot_wait_for_fulfillment, null) + spot_type = try(each.value.spot_type, var.defaults.spot_type, null) + spot_launch_group = try(each.value.spot_launch_group, var.defaults.spot_launch_group, null) + spot_block_duration_minutes = try(each.value.spot_block_duration_minutes, var.defaults.spot_block_duration_minutes, null) + spot_instance_interruption_behavior = try(each.value.spot_instance_interruption_behavior, var.defaults.spot_instance_interruption_behavior, null) + spot_valid_until = try(each.value.spot_valid_until, var.defaults.spot_valid_until, null) + spot_valid_from = try(each.value.spot_valid_from, var.defaults.spot_valid_from, null) + disable_api_stop = try(each.value.disable_api_stop, var.defaults.disable_api_stop, null) + putin_khuylo = try(each.value.putin_khuylo, var.defaults.putin_khuylo, true) + create_iam_instance_profile = try(each.value.create_iam_instance_profile, var.defaults.create_iam_instance_profile, false) + iam_role_name = try(each.value.iam_role_name, var.defaults.iam_role_name, null) + iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, var.defaults.iam_role_use_name_prefix, true) + iam_role_path = try(each.value.iam_role_path, var.defaults.iam_role_path, null) + iam_role_description = try(each.value.iam_role_description, var.defaults.iam_role_description, null) + iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, var.defaults.iam_role_permissions_boundary, null) + iam_role_policies = try(each.value.iam_role_policies, var.defaults.iam_role_policies, {}) + iam_role_tags = try(each.value.iam_role_tags, var.defaults.iam_role_tags, {}) } diff --git a/wrappers/outputs.tf b/wrappers/outputs.tf index 5da7c09b..ec6da5f4 100644 --- a/wrappers/outputs.tf +++ b/wrappers/outputs.tf @@ -1,5 +1,5 @@ output "wrapper" { description = "Map of outputs of a wrapper." value = module.wrapper - # sensitive = false # No sensitive module output found + # sensitive = false # No sensitive module output found }