iT邦幫忙

2023 iThome 鐵人賽

DAY 6
0
DevOps

大家都在用 Terraform 實作 IaC 為什麼不將程式寫得更簡潔易讀呢?系列 第 6

如何利用 HCL 語言的撰寫 Terraform 專案的基本介紹 (Part VI) - 模組間相依性

  • 分享至 

  • xImage
  •  

接下來,要建立兩個模組分別為 vpcsubnet 來介紹模組間相依性,由模組 vpc 生成後會產出 vpc_id 傳入 subnet 模組中,然後由模組 subnet 生成後會產出 subnet_id 傳入給 ec2_instances 模組中。

┌─────┐    vpc_id    ┌────────┐    subnet_id     ┌───────────────┐
│ vpc │ -----------> │ subnet │ ---------------> │ ec2_instances │
└─────┘              └────────┘                  └───────────────┘

專案檔案結構如下:

├── main.tf
├── modules
│   ├── ec2_instances
│   │   ├── data.aws_ami.ami.tf
│   │   ├── main.tf
│   │   ├── provider.tf
│   │   └── variables.tf
│   ├── subnet
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── provider.tf
│   │   └── variables.tf
│   └── vpc
│       ├── main.tf
│       ├── outputs.tf
│       ├── provider.tf
│       └── variables.tf
├── osaka.tfvars
├── terraform.tfstate
├── tokyo.tfvars
└── variables.tf

先來建立模組 vpc 相關的檔案如下:

  • modules/vpc/main.tf
resource "aws_vpc" "vpc" {
  assign_generated_ipv6_cidr_block = false
  cidr_block                       = var.vpc_cidr
  enable_dns_hostnames             = true
  enable_dns_support               = true
  instance_tenancy                 = "default"

  tags = {
    
    Name        = var.vpc_name
    Environment = var.environment
  }
}
  • modules/vpc/outputs.tf
output "vpc_id" {
  # 輸出建立完成後的 vpc_id
  value = aws_vpc.vpc.id
}
  • modules/vpc/provider.tf
provider "aws" {
  region  = var.aws_region
  profile = var.aws_profile
}
  • modules/vpc/variables.tf
variable "aws_region" {
  type        = string
  description = "AWS region"
}

variable "aws_profile" {
  type        = string
  description = "Profile in .aws/config"
}

variable "vpc_name" {
  type        = string
  description = "The name of vpc"
}

variable "vpc_cidr" {
  type        = string
  description = "The cidr of vpc"
}

variable "environment" {
  type = string
  description = "The environment of vpc"
}


再來建立模組 subnet 相關的檔案如下:

  • modules/subnet/main.tf
    ** depends_on 中設定 var.vpc_id 代表 subnet 模組會等待 vpc 被建立出來後才開始建立 **
resource "aws_subnet" "subnet" {
  assign_ipv6_address_on_creation = false
  availability_zone               = var.subnet_zone
  cidr_block                      = var.subnet_cidr
  ipv6_cidr_block                 = null
  customer_owned_ipv4_pool        = ""
  map_customer_owned_ip_on_launch = false
  map_public_ip_on_launch         = false
  outpost_arn                     = ""

  tags = {
    Name        = var.subnet_name
    Environment = var.environment
    
  }

  vpc_id = var.vpc_id

  depends_on = [
    var.vpc_id
  ]
}
  • modules/subnet/outputs.tf
output "subnet_id" {
  # 輸出建立完成後的 subnet_id
  value = aws_subnet.subnet.id
}
  • modules/subnet/provider.tf
provider "aws" {
  region  = var.aws_region
  profile = var.aws_profile
}
  • modules/subnet/variables.tf
variable "aws_region" {
  type        = string
  description = "AWS region"
}

variable "aws_profile" {
  type        = string
  description = "Profile in .aws/config"
}

variable "vpc_id" {
  type        = string
  description = "The vpc id of subnet"
}

variable "subnet_cidr" {
  type        = string
  description = "The cidr of subnet"
}

variable "subnet_name" {
  type        = string
  description = "The name of subnet"
}

variable "subnet_zone" {
  type        = string
  description = "The zone of subnet"
}

variable "environment" {
  type = string
  description = "The environment of subnet"
}

接著要來修該 ec2_instances module 要支持傳入 subnet_id 變數,需要修改的檔案如下:

  • modules/main.tf
    ** depends_on 中設定 var.subnet_id 代表 ec2_instnaces 模組會等待 subnet 被建立出來後才開始建立 **
resource "aws_instance" "example" {
  count         = var.instance_count
  ami           = (var.ami_id == null) ? data.aws_ami.selected_ami.id : var.ami_id
  instance_type = var.instance_type
  subnet_id     = var.subnet_id

  tags = {
    Name        = "ec2-instance-${count.index}"
    Environment = var.environment
  }

  depends_on = [ var.subnet_id ]
}

  • modules/variables.tf
...
# 新增 subnet_id 變數宣告
variable "subnet_id" {
  type = string
  description = "The subnet id of EC2 instance"
}

再來修改一下主程式 main.tf,且修改一下 tokyo.tfvars' 與 osaka.tfvars` 需要帶入的變數內容:

  • main.tf
terraform {
  required_providers {
    aws = {
      version = "5.15.0"
    }
  }
}

module "vpc" {
  source = "./modules/vpc"

  aws_region  = var.aws_region
  aws_profile = var.aws_profile

  vpc_name    = var.vpc_name
  vpc_cidr    = var.vpc_cidr
  environment = var.environment
}

module "subnet" {
  source = "./modules/subnet"

  aws_region  = var.aws_region
  aws_profile = var.aws_profile

  vpc_id      = module.vpc.vpc_id
  subnet_name = var.subnet_name
  subnet_cidr = var.subnet_cidr
  subnet_zone = var.subnet_zone
  environment = var.environment
}

module "my_ec2_instances" {
  source = "./modules/ec2_instances"

  aws_region  = var.aws_region
  aws_profile = var.aws_profile

  instance_count = var.instance_count
  instance_type  = var.instance_type
  ami_id         = var.ami_id
  environment    = var.environment
  subnet_id      = module.subnet.subnet_id
}

  • osaka.tfvars
aws_region     = "ap-northeast-3"
aws_profile    = "heretse"
vpc_cidr       = "10.1.0.0/16"
vpc_name       = "osaka-vpc"
subnet_cidr    = "10.1.1.0/24"
subnet_name    = "osaka-subnet"
subnet_zone    = "ap-northeast-3d"
instance_count = 2
instance_type  = "t3a.micro"
environment    = "Osaka"
  • tokyo.tfvars
aws_region     = "ap-northeast-1"
aws_profile    = "heretse"
vpc_cidr       = "10.0.0.0/16"
vpc_name       = "tokyo-vpc"
subnet_cidr    = "10.0.1.0/24"
subnet_name    = "tokyo-subnet"
subnet_zone    = "ap-northeast-1d"
instance_count = 1
instance_type  = "t3a.micro"
environment    = "Tokyo"

最後,來試著跑執行計畫來看一下相依性關係如何呈現吧!!

  1. terraform init: 初始化新建立的 modules。

Initializing the backend...
Initializing modules...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.15.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
  1. terraform plan --out .plan -var-file=tokyo.tfvars: 執行 Tokyo region 計畫。
module.my_ec2_instances.data.aws_ami.selected_ami: Reading...
module.my_ec2_instances.data.aws_ami.selected_ami: Read complete after 0s [id=ami-0f419d2f905bb344e]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

Terraform will perform the following actions:

  # module.my_ec2_instances.aws_instance.example[0] will be created
  + resource "aws_instance" "example" {
      + ami                                  = "ami-0f419d2f905bb344e"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t3a.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Environment" = "Tokyo"
          + "Name"        = "ec2-instance-0"
        }
      + tags_all                             = {
          + "Environment" = "Tokyo"
          + "Name"        = "ec2-instance-0"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)
    }

  # module.subnet.aws_subnet.subnet will be created
  + resource "aws_subnet" "subnet" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-1d"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_customer_owned_ip_on_launch                = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Environment" = "Tokyo"
          + "Name"        = "tokyo-subnet"
        }
      + tags_all                                       = {
          + "Environment" = "Tokyo"
          + "Name"        = "tokyo-subnet"
        }
      + vpc_id                                         = (known after apply)
    }

  # module.vpc.aws_vpc.vpc will be created
  + resource "aws_vpc" "vpc" {
      + arn                                  = (known after apply)
      + assign_generated_ipv6_cidr_block     = false
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = true
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags                                 = {
          + "Environment" = "Tokyo"
          + "Name"        = "tokyo-vpc"
        }
      + tags_all                             = {
          + "Environment" = "Tokyo"
          + "Name"        = "tokyo-vpc"
        }
    }

Plan: 3 to add, 0 to change, 0 to destroy.

───────────────────────────────────────────────────────────────────────

Saved the plan to: .plan

To perform exactly these actions, run the following command to apply:
    terraform apply ".plan"
  1. terraform plan --out .plan -var-file=osaka.tfvars:執行 Osaka region 計畫。
module.my_ec2_instances.data.aws_ami.selected_ami: Reading...
module.my_ec2_instances.data.aws_ami.selected_ami: Read complete after 0s [id=ami-035f22189112edf2e]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

Terraform will perform the following actions:

  # module.my_ec2_instances.aws_instance.example[0] will be created
  + resource "aws_instance" "example" {
      + ami                                  = "ami-035f22189112edf2e"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t3a.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Environment" = "Osaka"
          + "Name"        = "ec2-instance-0"
        }
      + tags_all                             = {
          + "Environment" = "Osaka"
          + "Name"        = "ec2-instance-0"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)
    }

  # module.my_ec2_instances.aws_instance.example[1] will be created
  + resource "aws_instance" "example" {
      + ami                                  = "ami-035f22189112edf2e"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t3a.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Environment" = "Osaka"
          + "Name"        = "ec2-instance-1"
        }
      + tags_all                             = {
          + "Environment" = "Osaka"
          + "Name"        = "ec2-instance-1"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)
    }

  # module.subnet.aws_subnet.subnet will be created
  + resource "aws_subnet" "subnet" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-3d"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.1.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_customer_owned_ip_on_launch                = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Environment" = "Osaka"
          + "Name"        = "osaka-subnet"
        }
      + tags_all                                       = {
          + "Environment" = "Osaka"
          + "Name"        = "osaka-subnet"
        }
      + vpc_id                                         = (known after apply)
    }

  # module.vpc.aws_vpc.vpc will be created
  + resource "aws_vpc" "vpc" {
      + arn                                  = (known after apply)
      + assign_generated_ipv6_cidr_block     = false
      + cidr_block                           = "10.1.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = true
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags                                 = {
          + "Environment" = "Osaka"
          + "Name"        = "osaka-vpc"
        }
      + tags_all                             = {
          + "Environment" = "Osaka"
          + "Name"        = "osaka-vpc"
        }
    }

Plan: 4 to add, 0 to change, 0 to destroy.

───────────────────────────────────────────────────────────────────────

Saved the plan to: .plan

To perform exactly these actions, run the following command to apply:
    terraform apply ".plan"

接下來,將介紹如何將 tfstate 用 AWS S3 儲存與使用 DynamoDB 表格作為 locks。


上一篇
如何利用 HCL 語言的撰寫 Terraform 專案的基本介紹 (Part V) - 使用 tfvars 多環境佈署
下一篇
如何利用 HCL 語言的撰寫 Terraform 專案的基本介紹 (Part VII) - 使用 AWS S3 儲存 Terraform State 與 AWS DynamoDB 作為 lock 機制
系列文
大家都在用 Terraform 實作 IaC 為什麼不將程式寫得更簡潔易讀呢?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言