From 49b8a5e00979332ca70a927d8a392c695c2e2cbb Mon Sep 17 00:00:00 2001 From: Piotr Stawarski Date: Wed, 29 May 2024 16:34:55 +0200 Subject: [PATCH] feat: initial version (#1) * code * docs * r53-vars * r53-records * docs * docs outputs * use data.aws_ec2_instance_type --- README.md | 66 +++++++-- examples/basic/main.tf | 48 +++++++ examples/basic/override.tf | 9 ++ examples/basic/terraform.tf | 12 ++ examples/example_of_use/.tool-versions | 1 - examples/example_of_use/README.mkdn | 63 --------- examples/example_of_use/main.tf | 4 - examples/example_of_use/versions.tf | 13 -- main.tf | 98 ++++++++++++- outputs.tf | 30 +++- variables.tf | 184 +++++++++++++++++++++++-- versions.tf | 13 -- 12 files changed, 422 insertions(+), 119 deletions(-) create mode 100644 examples/basic/main.tf create mode 100644 examples/basic/override.tf create mode 100644 examples/basic/terraform.tf delete mode 100644 examples/example_of_use/.tool-versions delete mode 100644 examples/example_of_use/README.mkdn delete mode 100644 examples/example_of_use/main.tf delete mode 100644 examples/example_of_use/versions.tf delete mode 100644 versions.tf diff --git a/README.md b/README.md index f6cb368..9c32ccd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Meet **OPSd**. The unique and effortless way of managing cloud infrastructure. -# terraform-module-template +# terraform-module-aws-instance ## Introduction @@ -12,11 +12,13 @@ What does the module provide? ```hcl module "module_name" { - source = "github.com/opsd-io/module_name?ref=v0.0.1" + source = "github.com/opsd-io/terraform-module-aws-instance" - # Variables - variable_1 = "foo" - variable_2 = "bar" + name = "my-host" + ami_id = data.aws_ami.debian11.id + instance_type = "t2.micro" + subnet_id = module.network.public_subnet_groups["public1"]["a"].id + key_name = aws_key_pair.me.key_name } ``` @@ -28,10 +30,13 @@ module "module_name" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.1 | +| [aws](#requirement\_aws) | ~> 5.0 | ## Providers -No providers. +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | ~> 5.0 | ## Modules @@ -39,15 +44,58 @@ No modules. ## Resources -No resources. +| Name | Type | +|------|------| +| [aws_instance.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | +| [aws_route53_record.private_cnames](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_route53_record.private_ip](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_route53_record.public_cnames](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_route53_record.public_ip](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_ec2_instance_type.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_instance_type) | data source | ## Inputs -No inputs. +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [ami\_id](#input\_ami\_id) | AMI to use for the instance. Overrides the AMI specified in the Launch Template. | `string` | `null` | no | +| [associate\_public\_ip\_address](#input\_associate\_public\_ip\_address) | Whether to associate a public IP address with an instance in a VPC. Overrides subnet setting. | `bool` | `null` | no | +| [common\_tags](#input\_common\_tags) | A map of tags to assign to every resource in this module. | `map(string)` | `{}` | no | +| [ebs\_optimized](#input\_ebs\_optimized) | If true, the launched EC2 instance will be EBS-optimized. | `bool` | `null` | no | +| [enable\_resource\_name\_dns\_a\_record](#input\_enable\_resource\_name\_dns\_a\_record) | Indicates whether to respond to DNS queries for instance hostnames with DNS A records. Overrides subnet setting. | `bool` | `null` | no | +| [enable\_resource\_name\_dns\_aaaa\_record](#input\_enable\_resource\_name\_dns\_aaaa\_record) | Indicates whether to respond to DNS queries for instance hostnames with DNS AAAA records. Overrides subnet setting. | `bool` | `null` | no | +| [hostname\_type](#input\_hostname\_type) | Type of hostname for Amazon EC2 instances. Valid values: ip-name and resource-name. Overrides subnet setting. | `string` | `null` | no | +| [iam\_instance\_profile](#input\_iam\_instance\_profile) | IAM Instance Profile to launch the instance with. | `string` | `null` | no | +| [instance\_type](#input\_instance\_type) | Instance type to use for the instance. Overrides the instance type specified in the Launch Template. | `string` | `null` | no | +| [key\_name](#input\_key\_name) | Key name of the Key Pair to use for the instance. | `string` | `null` | no | +| [monitoring](#input\_monitoring) | If true, the launched EC2 instance will have detailed monitoring enabled. | `bool` | `false` | no | +| [name](#input\_name) | The 'Name' tag of instance. | `string` | n/a | yes | +| [private\_zone\_id](#input\_private\_zone\_id) | The ID of the hosted zone to contain private IP record. | `string` | `null` | no | +| [private\_zone\_record\_cnames](#input\_private\_zone\_record\_cnames) | The CNAMEs of the private zone record. | `list(string)` | `[]` | no | +| [private\_zone\_record\_name](#input\_private\_zone\_record\_name) | The name of the private zone record. Fallback to instance name. | `string` | `null` | no | +| [private\_zone\_record\_ttl](#input\_private\_zone\_record\_ttl) | The TTL of the private records. | `number` | `600` | no | +| [public\_zone\_id](#input\_public\_zone\_id) | The ID of the hosted zone to contain public IP record, if any. | `string` | `null` | no | +| [public\_zone\_record\_cnames](#input\_public\_zone\_record\_cnames) | The CNAMEs of the public zone record. | `list(string)` | `[]` | no | +| [public\_zone\_record\_name](#input\_public\_zone\_record\_name) | The name of the public zone record. Fallback to instance name. | `string` | `null` | no | +| [public\_zone\_record\_ttl](#input\_public\_zone\_record\_ttl) | The TTL of the public records. | `number` | `600` | no | +| [root\_iops](#input\_root\_iops) | Amount of provisioned IOPS. Only valid for root\_volume\_type of io1, io2 or gp3. Defaults to AMI setting. | `number` | `null` | no | +| [root\_volume\_encryption](#input\_root\_volume\_encryption) | Whether to enable root volume encryption. Defaults to AMI setting. | `bool` | `null` | no | +| [root\_volume\_size](#input\_root\_volume\_size) | Size of the root volume in gibibytes (GiB). Defaults to AMI setting. | `number` | `null` | no | +| [root\_volume\_type](#input\_root\_volume\_type) | Type of root volume. Defaults to AMI setting. | `string` | `null` | no | +| [security\_group\_ids](#input\_security\_group\_ids) | List of security group IDs to associate with. | `list(string)` | `[]` | 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. | `bool` | `true` | no | +| [subnet\_id](#input\_subnet\_id) | VPC Subnet ID to launch in. | `string` | n/a | yes | +| [user\_data](#input\_user\_data) | User data to provide when launching the instance. | `string` | `null` | no | +| [user\_data\_base64](#input\_user\_data\_base64) | User data to provide when launching the instance. | `string` | `null` | no | ## Outputs -No outputs. +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The ARN of the instance. | +| [id](#output\_id) | The ID of the instance. | +| [private\_ip](#output\_private\_ip) | The private IP of the instance. | +| [public\_ip](#output\_public\_ip) | The public IP of the instance. | +| [root\_volume\_id](#output\_root\_volume\_id) | The ID of the root volume. | ## Examples of usage diff --git a/examples/basic/main.tf b/examples/basic/main.tf new file mode 100644 index 0000000..c2ab0b6 --- /dev/null +++ b/examples/basic/main.tf @@ -0,0 +1,48 @@ +data "aws_ami" "debian11" { + most_recent = true + owners = ["amazon"] + + filter { + name = "name" + values = ["debian-12-amd64-*"] + } +} + +module "network" { + source = "github.com/opsd-io/terraform-module-aws-network" + + vpc_name = "test-vpc" + cidr_block = "10.100.0.0/16" + + public_subnet_groups = { + "public1" = { + availability_zones = { + "a" = { cidr_block = "10.100.1.0/24" } + "b" = { cidr_block = "10.100.2.0/24" } + "c" = { cidr_block = "10.100.3.0/24" } + } + } + } +} + +resource "aws_key_pair" "me" { + public_key = file("~/.ssh/id_rsa.pub") +} + +module "vm_bastion" { + source = "github.com/opsd-io/terraform-module-aws-instance" + + name = "bastion" + ami_id = data.aws_ami.debian11.id + instance_type = "t2.micro" + subnet_id = module.network.public_subnet_groups["public1"]["a"].id + key_name = aws_key_pair.me.key_name +} + +output "network" { + value = module.network +} + +output "vm_bastion" { + value = module.vm_bastion +} diff --git a/examples/basic/override.tf b/examples/basic/override.tf new file mode 100644 index 0000000..9861371 --- /dev/null +++ b/examples/basic/override.tf @@ -0,0 +1,9 @@ +# Make sure we're using working version (from local directory, not git). + +module "vm_bastion" { + source = "./../.." +} + +module "network" { + source = "/Users/stawi/work/opsd/terraform-module-aws-network" +} diff --git a/examples/basic/terraform.tf b/examples/basic/terraform.tf new file mode 100644 index 0000000..2077345 --- /dev/null +++ b/examples/basic/terraform.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = "eu-central-1" +} diff --git a/examples/example_of_use/.tool-versions b/examples/example_of_use/.tool-versions deleted file mode 100644 index 0743dde..0000000 --- a/examples/example_of_use/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -terraform 1.5.5 diff --git a/examples/example_of_use/README.mkdn b/examples/example_of_use/README.mkdn deleted file mode 100644 index fc28c30..0000000 --- a/examples/example_of_use/README.mkdn +++ /dev/null @@ -1,63 +0,0 @@ -# Example title - -Brief description of an example. - -## Required tools - -### AZ CLI - -Before you start, you need to install Azure CLI according to the official [documentation](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli). - -Use the `az login` command to login to your Azure subscription. - -### asdf and direnv - -Additinaly `asdf` and `direnv` must be installed and configured according to their official documentation. - -- [asdf](https://asdf-vm.com/) - manages multiple runtime versions; -- [direnv](https://direnv.net/) - augments existing shells with a new feature that can load and unload environment variables. - -### Terraform - -The suggested Terraform version is defined in the [.tool-versions](.tool-versions) file. -To install it, simply execute - -```bash -asdf install terraform -``` - -## Terraforming infrastructure - -To start working with the module, you need to initialize terraform. - -```shell -terraform init -``` - -Execute plan command. - -```shell -terraform plan -``` - -and verify what will be created. - -The last step is to create the repo - -```shell -terrafrorm apply -``` - -**IMPORTANT**: Please double-check the command output. The vital section can be seen in the example `Plan: 6 to add, 0 to change, 0 to destroy`. Ensure that you understand the changes you are making. - -Next, you will be asked - -```shell -Do you want to perform these actions? - Terraform will perform the actions described above. - Only 'yes' will be accepted to approve. - - Enter a value: -``` - -Type `yes` to approve and let the magic happen. diff --git a/examples/example_of_use/main.tf b/examples/example_of_use/main.tf deleted file mode 100644 index 9eda987..0000000 --- a/examples/example_of_use/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -module "module_name" { - source = "../../" - -} diff --git a/examples/example_of_use/versions.tf b/examples/example_of_use/versions.tf deleted file mode 100644 index b7d3369..0000000 --- a/examples/example_of_use/versions.tf +++ /dev/null @@ -1,13 +0,0 @@ -terraform { - required_version = ">= 1.3.1" - required_providers { - # github = { - # source = "integrations/github" - # version = ">= 5.3.0" - # } - # azurerm = { - # source = "hashicorp/azurerm" - # version = ">= 3.22.0" - # } - } -} diff --git a/main.tf b/main.tf index 1c6a2e5..c361f01 100644 --- a/main.tf +++ b/main.tf @@ -1 +1,97 @@ -# Terraform code goes here +terraform { + required_version = ">= 1.3.1" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +data "aws_ec2_instance_type" "main" { + instance_type = var.instance_type +} + +resource "aws_instance" "main" { + ami = var.ami_id + instance_type = var.instance_type + ebs_optimized = var.ebs_optimized == null ? null : (var.ebs_optimized && data.aws_ec2_instance_type.main.ebs_optimized_support == "supported") + + user_data = var.user_data + user_data_base64 = var.user_data_base64 + user_data_replace_on_change = true # !!!! + + associate_public_ip_address = var.associate_public_ip_address + iam_instance_profile = var.iam_instance_profile + key_name = var.key_name + monitoring = var.monitoring + source_dest_check = var.source_dest_check + subnet_id = var.subnet_id + vpc_security_group_ids = length(var.security_group_ids) > 0 ? var.security_group_ids : null + + # launch_template {} + + private_dns_name_options { + hostname_type = var.hostname_type + enable_resource_name_dns_a_record = var.enable_resource_name_dns_a_record + enable_resource_name_dns_aaaa_record = var.enable_resource_name_dns_aaaa_record + } + + root_block_device { + delete_on_termination = true + encrypted = var.root_volume_encryption == null ? null : (var.root_volume_encryption && data.aws_ec2_instance_type.main.ebs_encryption_support == "supported") + volume_type = var.root_volume_type + volume_size = var.root_volume_size + iops = var.root_iops + tags = merge(var.common_tags, { + Name = "${var.name}-root" + }) + } + + # ebs_block_device {} + + # ephemeral_block_device {} + + tags = merge(var.common_tags, { + Name = var.name + }) + +} + +## Route53 records. + +resource "aws_route53_record" "private_ip" { + count = var.private_zone_id != null ? 1 : 0 + zone_id = var.private_zone_id + name = coalesce(var.private_zone_record_name, var.name) + type = "A" + ttl = var.private_zone_record_ttl + records = [aws_instance.main.private_ip] +} + +resource "aws_route53_record" "public_ip" { + count = var.public_zone_id != null ? 1 : 0 + zone_id = var.public_zone_id + name = coalesce(var.public_zone_record_name, var.name) + type = "A" + ttl = var.public_zone_record_ttl + records = [aws_instance.main.public_ip] +} + +resource "aws_route53_record" "private_cnames" { + for_each = toset(var.private_zone_id != null ? var.private_zone_record_cnames : []) + zone_id = var.private_zone_id + name = each.value + type = "CNAME" + ttl = var.private_zone_record_ttl + records = [aws_route53_record.private_ip[0].fqdn] +} + +resource "aws_route53_record" "public_cnames" { + for_each = toset(var.public_zone_id != null ? var.public_zone_record_cnames : []) + zone_id = var.public_zone_id + name = each.value + type = "CNAME" + ttl = var.public_zone_record_ttl + records = [aws_route53_record.public_ip[0].fqdn] +} diff --git a/outputs.tf b/outputs.tf index 47cec5f..f3f1b35 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,4 +1,28 @@ -# output "variable" { -# description = "output variable description" -# value = variable.main.name +# output "instance" { +# value = aws_instance.main # } + +output "id" { + description = "The ID of the instance." + value = aws_instance.main.id +} + +output "arn" { + description = "The ARN of the instance." + value = aws_instance.main.arn +} + +output "private_ip" { + description = "The private IP of the instance." + value = aws_instance.main.private_ip +} + +output "public_ip" { + description = "The public IP of the instance." + value = aws_instance.main.public_ip +} + +output "root_volume_id" { + description = "The ID of the root volume." + value = one(aws_instance.main.root_block_device[*].volume_id) +} diff --git a/variables.tf b/variables.tf index a27d76b..750cf3a 100644 --- a/variables.tf +++ b/variables.tf @@ -1,12 +1,172 @@ -# variable "variable_name" { -# description = "variable description" -# type = number -# default = 1 -# } - -# variable "variable_password" { -# description = "variable description" -# type = string -# sensitive = true -# default = "abc" -# } +variable "common_tags" { + description = "A map of tags to assign to every resource in this module." + type = map(string) + default = {} +} + +variable "name" { + description = "The 'Name' tag of instance." + type = string +} + +variable "ami_id" { + description = "AMI to use for the instance. Overrides the AMI specified in the Launch Template." + type = string + default = null +} + +variable "instance_type" { + description = "Instance type to use for the instance. Overrides the instance type specified in the Launch Template." + type = string + default = null +} + +variable "ebs_optimized" { + description = "If true, the launched EC2 instance will be EBS-optimized." + type = bool + default = null +} + + +variable "root_volume_encryption" { + description = "Whether to enable root volume encryption. Defaults to AMI setting." + type = bool + default = null +} + +variable "root_volume_type" { + description = "Type of root volume. Defaults to AMI setting." + type = string + default = null +} + +variable "root_volume_size" { + description = "Size of the root volume in gibibytes (GiB). Defaults to AMI setting." + type = number + default = null +} + +variable "root_iops" { + description = "Amount of provisioned IOPS. Only valid for root_volume_type of io1, io2 or gp3. Defaults to AMI setting." + type = number + default = null +} + +variable "subnet_id" { + description = "VPC Subnet ID to launch in." + type = string +} + +variable "associate_public_ip_address" { + description = "Whether to associate a public IP address with an instance in a VPC. Overrides subnet setting." + type = bool + default = null +} + +variable "hostname_type" { + description = "Type of hostname for Amazon EC2 instances. Valid values: ip-name and resource-name. Overrides subnet setting." + type = string + default = null +} + +variable "enable_resource_name_dns_a_record" { + description = "Indicates whether to respond to DNS queries for instance hostnames with DNS A records. Overrides subnet setting." + type = bool + default = null +} + +variable "enable_resource_name_dns_aaaa_record" { + description = "Indicates whether to respond to DNS queries for instance hostnames with DNS AAAA records. Overrides subnet setting." + type = bool + default = null +} + +variable "source_dest_check" { + description = "Controls if traffic is routed to the instance when the destination address does not match the instance." + type = bool + default = true +} + +variable "key_name" { + description = "Key name of the Key Pair to use for the instance." + type = string + default = null +} + +variable "user_data" { + description = "User data to provide when launching the instance." + type = string + default = null +} + +variable "user_data_base64" { + description = "User data to provide when launching the instance." + type = string + default = null +} + +variable "iam_instance_profile" { + description = "IAM Instance Profile to launch the instance with." + type = string + default = null +} + +variable "monitoring" { + description = "If true, the launched EC2 instance will have detailed monitoring enabled." + type = bool + default = false +} + +variable "security_group_ids" { + description = "List of security group IDs to associate with." + type = list(string) + default = [] +} + +variable "private_zone_id" { + description = "The ID of the hosted zone to contain private IP record." + type = string + default = null +} + +variable "public_zone_id" { + description = "The ID of the hosted zone to contain public IP record, if any." + type = string + default = null +} + +variable "private_zone_record_name" { + description = "The name of the private zone record. Fallback to instance name." + type = string + default = null +} + +variable "public_zone_record_name" { + description = "The name of the public zone record. Fallback to instance name." + type = string + default = null +} + +variable "private_zone_record_cnames" { + description = "The CNAMEs of the private zone record." + type = list(string) + default = [] +} + +variable "public_zone_record_cnames" { + description = "The CNAMEs of the public zone record." + type = list(string) + default = [] +} + +variable "private_zone_record_ttl" { + description = "The TTL of the private records." + type = number + default = 600 +} + +variable "public_zone_record_ttl" { + description = "The TTL of the public records." + type = number + default = 600 +} diff --git a/versions.tf b/versions.tf deleted file mode 100644 index b7d3369..0000000 --- a/versions.tf +++ /dev/null @@ -1,13 +0,0 @@ -terraform { - required_version = ">= 1.3.1" - required_providers { - # github = { - # source = "integrations/github" - # version = ">= 5.3.0" - # } - # azurerm = { - # source = "hashicorp/azurerm" - # version = ">= 3.22.0" - # } - } -}