接下來,要建立兩個模組分別為 vpc
和 subnet
來介紹模組間相依性,由模組 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` 需要帶入的變數內容:
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
}
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"
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"
最後,來試著跑執行計畫來看一下相依性關係如何呈現吧!!
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.
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"
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。