本篇是實作常用的 AWS CloudWatch 與 EventBridge 服務之 Terraform 模組,並且會使用到 YAML 資料結構來定義模組的內容,完整的專案程式碼分享在我的 Github 上。
./configs/cloudwatch/loggroups.yaml 與 模組 my_cloudfront 的放置位置 modules/my_cloudwatch:├── configs
│   ├── cloudfront
│   │   └── distributions.yaml
│   ├── cloudwatch
│   │   └── loggroups.yaml
│   ├── iam
│   │   ├── assume_role_policies
│   │   ├── policies
│   │   ├── role_policies
│   │   ├── user_policies
│   │   └── iam.yaml
│   ├── s3
│   │   ├── policies
│   │   └── s3.yaml
│   ├── subnet
│   │   └── my-subnets.yaml
│   └── vpc
│       └── my-vpcs.yaml
├── example.tfvars
├── locals.tf
├── main.tf
├── modules
│   ├── my_cloudfront
│   ├── my_cloudwatch
│   │   ├── cloudwatch.tf
│   │   ├── outputs.tf
│   │   ├── provider.tf
│   │   └── variables.tf
│   ├── my_eips
│   ├── my_eventbridge
│   │   ├── outputs.tf
│   │   ├── provider.tf
│   │   ├── rule.tf
│   │   ├── target.tf
│   │   └── variables.tf
│   ├── my_iam
│   ├── my_igw
│   ├── my_instances
│   ├── my_nacls
│   ├── my_route_tables
│   ├── my_s3
│   ├── my_subnets
│   └── my_vpc
└── variables.tf
./configs/eventbridge/events.yaml、模組 my_eventbridge 的放置位置 modules/my_eventbridge 和放置 event pattern JSON file 的目錄 ./configs/eventbridge/patterns :├── configs
├── cloudwatch
│   └── cloudwatch.yaml
├── eventbridge
│   ├── events.yaml
│   └── patterns
│   ├── iam
│   │   ├── assume_role_policies
│   │   ├── policies
│   │   ├── role_policies
│   │   ├── user_policies
│   │   └── iam.yaml
│   ├── s3
│   │   ├── policies
│   │   └── s3.yaml
│   ├── subnet
│   │   └── my-subnets.yaml
│   └── vpc
│       └── my-vpcs.yaml
├── example.tfvars
├── locals.tf
├── main.tf
├── modules
│   ├── my_cloudwatch
│   │   ├── cloudwatch.tf
│   │   ├── outputs.tf
│   │   ├── provider.tf
│   │   └── variables.tf
│   ├── my_eips
│   ├── my_eventbridge
│   │   ├── outputs.tf
│   │   ├── provider.tf
│   │   ├── rule.tf
│   │   ├── target.tf
│   │   └── variables.tf
│   ├── my_iam
│   ├── my_igw
│   ├── my_instances
│   ├── my_nacls
│   ├── my_route_tables
│   ├── my_s3
│   ├── my_subnets
│   └── my_vpc
└── variables.tf
./configs/cloudwatch/cloudwatch.yaml 內容來定義 CloudWatch 需要用建立的資源:loggroups:
  - loggroup_name: "<LOGGROUP_NAME>"
    retention_in_days: <RETENTION_IN_DAYS>
./configs/eventbridge/eventbridge.yaml 內容來定義 EventBridge 需要用建立的資源:rules:
 - name: "<RULE_NAME>"
   description: "<RULE_DESCRIPTION>"
   event_bus_name: "<EVENT_BUS_NAME>"
   event_pattern_file: "<EVENT_PATTERN_FILE_PATH>"
   enabled: <true or false>
targets:
  - arn: "<TARGET_ARN>"
    rule_name: "<RULE_NAME>"
    target_id: "<TARGET_ID>"
my_cloudwatch 模組:./modules/my_cloudwatch/outputs.tf:output "loggroup" {
  value = aws_cloudwatch_log_group.log_group
}
./modules/my_cloudwatch/provider.tf:provider "aws" {
    region  = var.aws_region
    profile = var.aws_profile
}
./modules/my_cloudwatch/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 "cloudwatch_path" {
  description = "Cloudwatch path"
  default     = ""
}
./modules/my_cloudwatch/cloudwatch.tf:locals {
  cloudwatch_loggroups = yamldecode(file("${var.cloudwatch_path}"))["loggroups"]
}
resource "aws_cloudwatch_log_group" "log_group" {
  for_each = { for r in local.cloudwatch_loggroups : r.loggroup_name => r }
  name = each.value.loggroup_name
  retention_in_days = each.value.retention_in_days
  tags = {
    Name       = each.value.loggroup_name
    Department = var.department_name
    Project    = var.project_name
  }
  tags_all = {
    Name       = each.value.loggroup_name
    Department = var.department_name
    Project    = var.project_name
  }
}
my_eventbridge 模組:./modules/my_eventbridge/outputs.tf:output "rule" {
  value = aws_cloudwatch_event_rule.rule
}
output "target" {
  value = aws_cloudwatch_event_target.target
}
./modules/my_eventbridge/provider.tf:provider "aws" {
    region  = var.aws_region
    profile = var.aws_profile
}
./modules/my_eventbridge/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"
}
./modules/my_eventbridge/rule.tf:locals {
  rules = yamldecode(file("${var.eventbridge_path}"))["rules"]
}
resource "aws_cloudwatch_event_rule" "rule" {
  for_each = { for r in local.rules : r.name => r }
  name           = each.value.name
  description    = each.value.description
  event_bus_name = each.value.event_bus_name
  event_pattern  = file("${each.value.event_pattern_file}")
  is_enabled     = each.value.enabled
}
./modules/my_eventbridge/target.tf:locals {
  targets = yamldecode(file("${var.eventbridge_path}"))["targets"]
}
resource "aws_cloudwatch_event_target" "target" {
  for_each = { for r in local.targets : r.target_id => r }
  arn       = each.value.arn
  rule      = each.value.rule_name
  target_id = each.value.target_id
  depends_on = [
    aws_cloudwatch_event_rule.rule
  ]
}
example.tfvars:aws_region="ap-northeast-1"
aws_profile="<YOUR_PROFILE>"
project_name="example"
department_name="SRE"
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>"
  }
}
其他模組省略...
# cloudwatch loggroup
module "cloudwatch" {
  aws_profile     = var.aws_profile
  aws_region      = var.aws_region
  environment     = var.environment
  department_name = var.department_name
  project_name    = var.project_name
  cloudwatch_path = "./configs/cloudwatch/loggroups.yaml"
  source = "./modules/my_cloudwatch"
}
# cloudwatch eventbridge
module "EventBridge" {
  aws_profile = var.aws_profile
  aws_region  = var.aws_region
  eventbridge_path = "./configs/eventbridge/events.yaml"
  source = "./modules/my_eventbridge"
}
loggroups:
  - loggroup_name: "/example/my_loggroup"
    retention_in_days: 7
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.cloudwatch.aws_cloudwatch_log_group.log_group["/example/my_loggroup"] will be created
  + resource "aws_cloudwatch_log_group" "log_group" {
      + arn               = (known after apply)
      + id                = (known after apply)
      + name              = "/example/my_loggroup"
      + name_prefix       = (known after apply)
      + retention_in_days = 7
      + skip_destroy      = false
      + tags              = {
          + "Department" = "SRE"
          + "Name"       = "/example/my_loggroup"
          + "Project"    = "example"
        }
      + tags_all          = {
          + "Department" = "SRE"
          + "Name"       = "/example/my_loggroup"
          + "Project"    = "example"
        }
    }
...
Plan: 48 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...
下一篇文章將會展示實作 AWS Kinesis Firehose 與 Stream 之 Terraform 模組。