iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
DevOps

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

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

  • 分享至 

  • xImage
  •  

AWS ECR 模組實作

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

  1. 先定義整個專案檔案結構設定檔 ./configs/ecr/repos.yaml 與模組 my_ecr 的放置位置 modules/my_ecr:
├── 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
│   ├── 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_dynamodb
│   ├── my_ecr
│   │   ├── ecr_lifecycle_policy.tf
│   │   ├── ecr_repository.tf
│   │   ├── ecr_repository_policy.tf
│   │   ├── provider.tf
│   │   └── variables.tf
│   ├── 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/ecr/repos.yaml 內容來定義 ECR 需要用建立的資源:
repos:
  - name: "<REPO_NAME>"
    lifecycle: "<LIFECYCLE_JSON_PATH>"
    permission: "<PERMISSION_JSON_PATH>"
    image_tag_mutable: <true or false>
    scan_on_push: <true or false>
  1. 撰寫 my_ecr 模組:
  • ./modules/my_ecr/provider.tf:
provider "aws" {
    region  = var.aws_region
    profile = var.aws_profile
}
  • ./modules/my_ecr/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 "repo_path" {
  description = "repo path"
  default     = ""
}

  • ``:
locals {
  repos = yamldecode(file("${var.repo_path}"))["repos"]
}

resource "aws_ecr_repository" "repos" {

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

  encryption_configuration {
    encryption_type = "AES256"
  }

  image_scanning_configuration {
    scan_on_push = lookup(each.value, "scan_on_push", "false")
  }

  image_tag_mutability = lookup(each.value, "image_tag_mutable", true) ? "MUTABLE" : "IMMUTABLE"
  name                 = each.value.name
}

  • ./modules/my_ecr/ecr_lifecycle_policy.tf:
resource "aws_ecr_lifecycle_policy" "repos_lifecycle_policy" {
  for_each = { for r in local.repos : r.name => r if r.lifecycle != "" }

  policy = file("${each.value.lifecycle}")

  repository = each.value.name

  depends_on = [
    aws_ecr_repository.repos
  ]
}

  • ./modules/my_ecr/ecr_repository_policy.tf:
resource "aws_ecr_repository_policy" "repos_policy" {
  for_each = { for r in local.repos : r.name => r if r.permission != "" }

  policy = file("${each.value.permission}")

  repository = each.value.name

  depends_on = [
    aws_ecr_repository.repos
  ]
}

  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>"
  }
}

其他模組省略...

# ecr
module "ecr" {
  aws_profile = var.aws_profile
  aws_region  = var.aws_region
  repo_path   = "./configs/ecr/repos.yaml"

  source = "./modules/my_ecr"
}


Terraform 執行計畫

  1. 嘗試建立一個 ECR Repository 來測試一下模組:
repos:
  - name: my-repo
    lifecycle: ./configs/ecr/lifecycle_policy.json
    permission: ./configs/ecr/permission_policy.json
    image_tag_mutable: true
    scan_on_push: true

  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.ecr.aws_ecr_lifecycle_policy.repos_lifecycle_policy["my-repo"] will be created
  + resource "aws_ecr_lifecycle_policy" "repos_lifecycle_policy" {
      + id          = (known after apply)
      + policy      = jsonencode(
            {
              + rules = [
                  + {
                      + action       = {
                          + type = "expire"
                        }
                      + description  = "Remove untagged images for 7 days"
                      + rulePriority = 1
                      + selection    = {
                          + countNumber = 7
                          + countType   = "sinceImagePushed"
                          + countUnit   = "days"
                          + tagStatus   = "untagged"
                        }
                    },
                ]
            }
        )
      + registry_id = (known after apply)
      + repository  = "my-repo"
    }

  # module.ecr.aws_ecr_repository.repos["my-repo"] will be created
  + resource "aws_ecr_repository" "repos" {
      + arn                  = (known after apply)
      + id                   = (known after apply)
      + image_tag_mutability = "MUTABLE"
      + name                 = "my-repo"
      + registry_id          = (known after apply)
      + repository_url       = (known after apply)
      + tags_all             = (known after apply)

      + encryption_configuration {
          + encryption_type = "AES256"
          + kms_key         = (known after apply)
        }

      + image_scanning_configuration {
          + scan_on_push = true
        }
    }

  # module.ecr.aws_ecr_repository_policy.repos_policy["my-repo"] will be created
  + resource "aws_ecr_repository_policy" "repos_policy" {
      + id          = (known after apply)
      + policy      = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = [
                          + "ecr:BatchCheckLayerAvailability",
                          + "ecr:BatchGetImage",
                          + "ecr:CompleteLayerUpload",
                          + "ecr:GetDownloadUrlForLayer",
                          + "ecr:InitiateLayerUpload",
                          + "ecr:PutImage",
                          + "ecr:UploadLayerPart",
                        ]
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = "arn:aws:iam::338584095359:root"
                        }
                      + Sid       = "AllowPushPull"
                    },
                ]
              + Version   = "2008-10-17"
            }
        )
      + registry_id = (known after apply)
      + repository  = "my-repo"
    }

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

下一篇文章將會展示實作 AWS Route53 之 Terraform 模組。


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

尚未有邦友留言

立即登入留言