iT邦幫忙

2023 iThome 鐵人賽

DAY 28
0
DevOps

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

實作 AWS 常用服務之 Terraform 模組系列 - Route53 篇

  • 分享至 

  • xImage
  •  

AWS Route53 模組實作

本篇是實作常用的 AWS Route53 服務之 Terraform 模組,並且會使用到 YAML 資料結構來定義模組的內容,完整的專案程式碼分享在我的 Github 上。

  1. 先定義整個專案檔案結構設定檔 ./configs/r53/records.yaml 與模組 my_dns 的放置位置 modules/my_dns:
├── configs
│   ├── cloudfront
│   │   └── distributions.yaml
│   ├── cloudwatch
│   │   └── loggroups.yaml
│   ├── dynamodb
│   │   └── configurations.yaml
│   ├── ecr
│   │   ├── lifecycle_policy.json
│   │   ├── permission_policy.json
│   │   └── repos.yaml
│   ├── iam
│   │   ├── assume_role_policies
│   │   │   ├── eks-cluster.json
│   │   │   ├── eks-fargate-pod-execution-role.json
│   │   │   └── eks-node-group.json
│   │   ├── iam.yaml
│   │   ├── role_policies
│   │   │   └── eks-cluster-cloudwatch-metrics.json
│   │   └── user_policies
│   │       └── admin_access.json
│   ├── kinesis
│   │   └── streams.yaml
│   ├── kms
│   │   ├── keys.yaml
│   │   └── policies
│   │       └── my-key-policy.json
│   ├── r53
│   │   └── records.yaml
│   ├── s3
│   │   ├── policies
│   │   │   └── my-bucket.json
│   │   └── s3.yaml
│   ├── subnet
│   │   └── my-subnets.yaml
│   └── vpc
│       └── my-vpcs.yaml
├── example.tfvars
├── locals.tf
├── main.tf
├── modules
│   ├── my_aws_load_balancer_controller
│   ├── my_cloudfront
│   ├── my_cloudwatch
│   ├── my_dns
│   │   ├── main.tf
│   │   ├── provider.tf
│   │   └── variables.tf
│   ├── my_dns
│   ├── my_dynamodb
│   ├── my_ecr
│   ├── my_eips
│   ├── my_eks
│   ├── my_eventbridge
│   ├── my_iam
│   ├── my_igw
│   ├── my_instances
│   ├── my_karpenter
│   ├── my_kinesis_stream
│   ├── my_kms
│   ├── my_msk
│   ├── my_nacls
│   ├── my_route_tables
│   ├── my_s3
│   ├── my_subnets
│   └── my_vpc
├── my-ingress-controller-values.yaml
├── my-ingress-node-red.yaml
├── packer
│   └── apache-cassandra
└── variables.tf
  1. 撰寫 ./configs/r53/records.yaml 內容來定義 AWS Route53 需要用建立的資源:
alias: 
  - alias_target:
      dns_name: "<ALIAS_DNS_NAME>"
      evaluate_target_health: <true or false>
      hosted_zone_id: Z31USIVHYNEOWT
    name: "<ALIAS_DNS_NAME>"
    type: <"A" or "CNAME">

simple:
  - name: "<SIMPLE_NAME>"
    records:
      - <RECORD>
    ttl: <TTL>
    type: <"A" or "CNAME">

tags: []

vpcs:
- vpc_id: vpc-0ee88268758ca392c
  vpc_region: ap-northeast-1

zone_id: Z04227961BT9BSTA95P7


  1. 撰寫 my_dns 模組:
  • ./modules/my_dns/provider.tf:
provider "aws" {
    region  = var.aws_region
    profile = var.aws_profile
}
  • ./modules/my_dns/variables.tf:
variable "aws_region" {
  description = "AWS region"
  default     = "ap-northeast-1"
}

variable "aws_profile" {
  description = "AWS profile"
  default     = ""
}

variable "project_name" {
  type    = string
  description = "Project name"
  default = ""
}

variable "department_name" {
  type        = string
  description = "Department name"
  default     = "SRE"
}

variable "domain_name" {
  type        = string
  description = "The domain name of records"
}

variable "record_path" {
  type        = string
  description = "The path of records"
}

  • ./modules/my_dns/main.tf:
resource "aws_route53_zone" "r53zone" {
  name = var.domain_name

  dynamic "vpc" {
    for_each = local.vpcs
    content {
      vpc_id     = vpc.value["vpc_id"]
      vpc_region = vpc.value["vpc_region"]
    }
  }

  tags = { for tag in local.tags : tag.key => tag.value }
}

locals {
  record_simple = yamldecode(file("${var.record_path}"))["simple"]
  record_alias  = yamldecode(file("${var.record_path}"))["alias"]
  vpcs          = yamldecode(file("${var.record_path}"))["vpcs"]
  tags          = yamldecode(file("${var.record_path}"))["tags"]
}

resource "aws_route53_record" "simples" {

  for_each = { for r in local.record_simple : r.name => r }

  zone_id = aws_route53_zone.r53zone.id
  name    = each.value.name
  type    = each.value.type
  ttl     = each.value.ttl
  records = each.value.records
}

resource "aws_route53_record" "aliases" {

  for_each = { for r in local.record_alias : r.name => r }

  zone_id = aws_route53_zone.r53zone.id
  name    = each.value.name
  type    = each.value.type

  alias {
    name                   = each.value.alias_target.dns_name
    zone_id                = each.value.alias_target.hosted_zone_id
    evaluate_target_health = each.value.alias_target.evaluate_target_health
  }
}

  1. 撰寫專案相關程式
  • example.tfvars:
aws_region="ap-northeast-1"
aws_profile="<YOUR_PROFILE>"
project_name="example"
department_name="SRE"
cassandra_root_password="<CASSANDRA_ROOT_PASSWORD>"
  • main.tf:
terraform {
  required_providers {
    aws = {
      version = "5.15.0"
    }
  }

  backend "s3" {
    bucket                  = "<YOUR_S3_BUCKET_NAME>"
    dynamodb_table          = "<YOUR_DYNAMODB_TABLE_NAME>"
    key                     = "terraform.tfstate"
    region                  = "ap-northeast-1"
    shared_credentials_file = "~/.aws/config"
    profile                 = "<YOUR_PROFILE>"
  }
}

其他模組省略...

# route53
module "dns_nxdnet" {
  aws_region  = var.aws_region
  aws_profile = var.aws_profile
  record_path = "./configs/r53/records.yaml"
  domain_name = "domain.my"
  
  source = "./modules/my_dns"
}


Terraform 執行計畫

  1. 嘗試建立一個 AWS R53Zone - domain.my 來測試一下模組:
alias: []

simple: []

tags: []

vpcs:
- vpc_id: vpc-049f62a6dc464cb1c
  vpc_region: ap-northeast-1
  1. 於專案目錄下執行 terraform init && terraform plan --out .plan -var-file=example.tfvars 來確認一下結果:

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.route53.aws_route53_zone.r53zone will be created
  + resource "aws_route53_zone" "r53zone" {
      + arn                 = (known after apply)
      + comment             = "Managed by Terraform"
      + force_destroy       = false
      + id                  = (known after apply)
      + name                = "domain.my"
      + name_servers        = (known after apply)
      + primary_name_server = (known after apply)
      + tags_all            = (known after apply)
      + zone_id             = (known after apply)

      + vpc {
          + vpc_id     = "vpc-049f62a6dc464cb1c"
          + vpc_region = "ap-northeast-1"
        }
    }

Plan: 1 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"
Releasing state lock. This may take a few moments...
  1. 於專案目錄下執行 terraform apply ".plan" 來跑執行計畫建立 AWS Route53 Zone 來確認一下結果:
module.route53.aws_route53_zone.r53zone: Creating...
module.route53.aws_route53_zone.r53zone: Still creating... [10s elapsed]
module.route53.aws_route53_zone.r53zone: Still creating... [20s elapsed]
module.route53.aws_route53_zone.r53zone: Still creating... [30s elapsed]
module.route53.aws_route53_zone.r53zone: Still creating... [40s elapsed]
module.route53.aws_route53_zone.r53zone: Still creating... [50s elapsed]
module.route53.aws_route53_zone.r53zone: Creation complete after 54s [id=Z0762953IV1MDMFQZDFM]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
  1. 於專案目錄下執行 terraform state show 'module.route53.aws_route53_zone.r53zone' 來確認一下建立完成的 AWS Route53 Zone ID:
# module.route53.aws_route53_zone.r53zone:
resource "aws_route53_zone" "r53zone" {
    arn                 = "arn:aws:route53:::hostedzone/Z0762953IV1MDMFQZDFM"
    comment             = "Managed by Terraform"
    force_destroy       = false
    id                  = "Z0762953IV1MDMFQZDFM"
    name                = "domain.my"
    name_servers        = [
        "ns-0.awsdns-00.com.",
        "ns-1024.awsdns-00.org.",
        "ns-1536.awsdns-00.co.uk.",
        "ns-512.awsdns-00.net.",
    ]
    primary_name_server = "ns-0.awsdns-00.com."
    tags_all            = {}
    zone_id             = "Z0762953IV1MDMFQZDFM"

    vpc {
        vpc_id     = "vpc-049f62a6dc464cb1c"
        vpc_region = "ap-northeast-1"
    }
}
  1. 再來嘗試建立 DNS Alias 與 Simple 記錄各一個來修改 ./configs/r53/records.yaml
alias:
  - alias_target:
      dns_name: k8s-nginxing-mynginxi-420ab563fa-0e4d90abe2740adb.elb.ap-northeast-1.amazonaws.com.
      evaluate_target_health: false
      hosted_zone_id: Z31USIVHYNEOWT
    name: alias-test
    type: CNAME

simple:
  - name: simple-test
    records:
      - 10.4.3.2
    ttl: 300
    type: A

tags: []

vpcs:
- vpc_id: vpc-049f62a6dc464cb1c
  vpc_region: ap-northeast-1

zone_id: Z04227961BT9BSTA95P7

  1. 再次於專案目錄下執行 terraform plan --out .plan -var-file=example.tfvars 來確認一下結果:
module.route53.aws_route53_zone.r53zone: Refreshing state... [id=Z0762953IV1MDMFQZDFM]

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.route53.aws_route53_record.aliases["alias-test"] will be created
  + resource "aws_route53_record" "aliases" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "alias-test"
      + type            = "CNAME"
      + zone_id         = "Z0762953IV1MDMFQZDFM"

      + alias {
          + evaluate_target_health = false
          + name                   = "k8s-nginxing-mynginxi-420ab563fa-0e4d90abe2740adb.elb.ap-northeast-1.amazonaws.com"
          + zone_id                = "Z31USIVHYNEOWT"
        }
    }

  # module.route53.aws_route53_record.simples["simple-test"] will be created
  + resource "aws_route53_record" "simples" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "simple-test"
      + records         = [
          + "10.4.3.2",
        ]
      + ttl             = 300
      + type            = "A"
      + zone_id         = "Z0762953IV1MDMFQZDFM"
    }

Plan: 2 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"
Releasing state lock. This may take a few moments...

下一篇文章將會介紹有哪些 Pre-commit 工具可以來檢查與管理 Terrform 專案。


上一篇
實作 AWS 常用服務之 Terraform 模組系列 - ECR 篇
下一篇
實作 AWS 常用服務之 Terraform 之 Pre-commit 工具篇
系列文
大家都在用 Terraform 實作 IaC 為什麼不將程式寫得更簡潔易讀呢?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言