今天要 import ECS 跟 CodePipeline 相關的 resource,分別把 ECS 的 resource 放到 container.tf
、CodePipeline 相關 resource 放到 codepipeline.tf
。(本日程式碼)
我們會先 import service discovery namespace:
import {
to = aws_service_discovery_http_namespace.app
id = "ns-oqfsu4obz4lghp3e"
}
這讓 ECS service 可以使用 AWS Cloud Map 來管理 service 的 DNS entry、讓 VPC 內的其他人可以找到這些 service。雖然本系列文不會用到它,但是 web console 有幫我們建立並且關聯到 cluster,所以還是 import 一下。
ECS cluster import block:
import {
to = aws_ecs_cluster.cluster
id = "my-app"
}
generated configuration:
resource "aws_ecs_cluster" "cluster" {
name = "my-app"
tags = {}
tags_all = {}
configuration {
execute_command_configuration {
kms_key_id = null
logging = "DEFAULT"
}
}
service_connect_defaults {
namespace = "arn:aws:servicediscovery:ap-northeast-1:xxxxxxxx:namespace/ns-oqfsu4obz4lghp3e"
}
setting {
name = "containerInsights"
value = "disabled"
}
}
import block:
import {
to = aws_ecs_task_definition.td
id = "arn:aws:ecs:ap-northeast-1:xxxxxxx:task-definition/my-app:5"
}
注意 id
最後的 revision,我們要 import 最新的 revision。
generated configuration:
resource "aws_ecs_task_definition" "td" {
container_definitions = "[{\"cpu\":0,\"environment\":[],\"environmentFiles\":[],\"essential\":true,\"image\":\"xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-app@sha256:61230330c3d91df6266740cb27174953c7bc1e5a3e3cc9dd709e546b276c5947\",\"mountPoints\":[],\"name\":\"my-app\",\"portMappings\":[{\"appProtocol\":\"http\",\"containerPort\":80,\"hostPort\":0,\"name\":\"my-app-80-tcp\",\"protocol\":\"tcp\"}],\"ulimits\":[],\"volumesFrom\":[]}]"
cpu = "1024"
execution_role_arn = null
family = "my-app"
ipc_mode = null
memory = "512"
network_mode = "bridge"
pid_mode = null
requires_compatibilities = ["EC2"]
skip_destroy = null
tags = {}
tags_all = {}
task_role_arn = null
runtime_platform {
cpu_architecture = "X86_64"
operating_system_family = "LINUX"
}
}
可以看到 container 的 definition 就搞成一串很難看的字串…..如果很受不了,可以用 HCL syntax 改寫,然後用 jsonencode()
包起來,會比較容易閱讀。
import block:
import {
to = aws_ecs_service.service
id = "my-app/my-app"
}
id 是 ecs cluster 名稱/ecs service 名稱
,一樣可以在 文件 查到。
generated configuration:
resource "aws_ecs_service" "service" {
cluster = "arn:aws:ecs:ap-northeast-1:xxxxxxx:cluster/my-app"
deployment_maximum_percent = 200
deployment_minimum_healthy_percent = 100
desired_count = 0
enable_ecs_managed_tags = true
enable_execute_command = false
force_new_deployment = null
health_check_grace_period_seconds = 0
iam_role = "/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS"
launch_type = "EC2"
name = "my-app"
platform_version = null
propagate_tags = "NONE"
scheduling_strategy = "REPLICA"
tags = {}
tags_all = {}
task_definition = "my-app:5"
triggers = {}
wait_for_steady_state = null
deployment_circuit_breaker {
enable = true
rollback = true
}
deployment_controller {
type = "ECS"
}
load_balancer {
container_name = "my-app"
container_port = 80
elb_name = null
target_group_arn = "arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxx:targetgroup/tg-my-app/8b85ca755abbba68"
}
ordered_placement_strategy {
field = "attribute:ecs.availability-zone"
type = "spread"
}
ordered_placement_strategy {
field = "instanceId"
type = "spread"
}
}
像上面的 cluster
、iam_role
、task_definition
等等參數,只要可以改用 resource 的 reference 就改成 reference 比較好,不然這份 configuration 等於是 hardcode 的,會無法 reuse。
IAM role 跟 IAM policy 的 import block:
import {
to = aws_iam_role.code_build_project
id = "codebuild-my-app-service-role"
}
import {
to = aws_iam_policy.code_build_project
id = "arn:aws:iam::384137370331:policy/service-role/CodeBuildBasePolicy-my-app-ap-northeast-1"
}
IAM policy generated config:
resource "aws_iam_policy" "code_build_project" {
description = "Policy used in trust relationship with CodeBuild"
name = "CodeBuildBasePolicy-my-app-ap-northeast-1"
name_prefix = null
path = "/service-role/"
policy = "{\"Statement\":[{\"Action\":[\"logs:CreateLogGroup\",\"logs:CreateLogStream\",\"logs:PutLogEvents\"],\"Effect\":\"Allow\",\"Resource\":[\"arn:aws:logs:ap-northeast-1:xxxxxxx:log-group:/aws/codebuild/my-app\",\"arn:aws:logs:ap-northeast-1:xxxxxxx:log-group:/aws/codebuild/my-app:*\"]},{\"Action\":[\"s3:PutObject\",\"s3:GetObject\",\"s3:GetObjectVersion\",\"s3:GetBucketAcl\",\"s3:GetBucketLocation\"],\"Effect\":\"Allow\",\"Resource\":[\"arn:aws:s3:::codepipeline-ap-northeast-1-*\"]},{\"Action\":[\"codebuild:CreateReportGroup\",\"codebuild:CreateReport\",\"codebuild:UpdateReport\",\"codebuild:BatchPutTestCases\",\"codebuild:BatchPutCodeCoverages\"],\"Effect\":\"Allow\",\"Resource\":[\"arn:aws:codebuild:ap-northeast-1:xxxxxxxxx:report-group/my-app-*\"]}],\"Version\":\"2012-10-17\"}"
tags = {}
tags_all = {}
}
可以看到上面 policy 超級多,這也是筆者一開始會選擇 import 的原因之一。如果對於各種 AWS 服務跟 IAM policy 還不熟悉,光是權限問題就可以把人逼瘋(O)。直接開最高權限當然可以解決使用問題,然後製造安全上的問題(O),權限設定的原則是「所需要的最少權限」而不是為了方便大家通通有最高權限。
如果不透過 web console 直接建立再 import,還有兩個方法可以寫出 policy:第一是找找看 AWS 文件有沒有現成範例,第二則是要執行的操作跑下去(像這邊是 codebuild 的 project),從錯誤訊息看缺什麼就補什麼,補到操作可以順利完成 (所謂的把人逼瘋…)。
import block:
import {
to = aws_codebuild_project.build_img
id = "my-app"
}
generated configuration:
resource "aws_codebuild_project" "build_img" {
badge_enabled = false
build_timeout = 60
concurrent_build_limit = 0
description = null
encryption_key = "arn:aws:kms:ap-northeast-1:xxxxxx:alias/aws/s3"
name = "my-app"
project_visibility = "PRIVATE"
queued_timeout = 480
resource_access_role = null
service_role = "arn:aws:iam::xxxxxx:role/service-role/codebuild-my-app-service-role"
source_version = null
tags = {}
tags_all = {}
artifacts {
artifact_identifier = null
bucket_owner_access = null
encryption_disabled = false
location = null
name = "my-app"
namespace_type = null
override_artifact_name = false
packaging = "NONE"
path = null
type = "CODEPIPELINE"
}
cache {
location = null
modes = []
type = "NO_CACHE"
}
environment {
certificate = null
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/standard:7.0"
image_pull_credentials_type = "CODEBUILD"
privileged_mode = false
type = "LINUX_CONTAINER"
}
logs_config {
cloudwatch_logs {
group_name = null
status = "DISABLED"
stream_name = null
}
s3_logs {
bucket_owner_access = null
encryption_disabled = false
location = null
status = "DISABLED"
}
}
source {
buildspec = "version: 0.2\nphases:\n build:\n commands:\n - ContainerName=\"my-app\"\n - ImageURI=$(cat imageDetail.json | jq -r '.ImageURI')\n - printf '[{\"name\":\"CONTAINER_NAME\",\"imageUri\":\"IMAGE_URI\"}]' > imagedefinitions.json\n - sed -i -e \"s|CONTAINER_NAME|$ContainerName|g\" imagedefinitions.json\n - sed -i -e \"s|IMAGE_URI|$ImageURI|g\" imagedefinitions.json\n \nartifacts:\n files:\n - imagedefinitions.json"
git_clone_depth = 0
insecure_ssl = false
location = null
report_build_status = false
type = "CODEPIPELINE"
}
}
遇到 error:
直接把 concurrent_build_limit
刪掉。
IAM role 及 policy 的 import block:
import {
to = aws_iam_role.codepipeline
id = "AWSCodePipelineServiceRole-ap-northeast-1-my-app"
}
import {
to = aws_iam_policy.codepipeline
id = "arn:aws:iam::xxxxxxx:policy/service-role/AWSCodePipelineServiceRole-ap-northeast-1-my-app"
}
generated configuration 因為 policy 很多很多所以很長,就不貼了~這邊的 import 都不太有遇到什麼 error。
import block:
import {
to = aws_codepipeline.pipeline
id = "my-app"
}
generated HCL:
resource "aws_codepipeline" "pipeline" {
name = "my-app"
role_arn = "arn:aws:iam::xxxxxx:role/service-role/AWSCodePipelineServiceRole-ap-northeast-1-my-app"
tags = {}
tags_all = {}
artifact_store {
location = "codepipeline-ap-northeast-1-239650800043"
region = null
type = "S3"
}
stage {
name = "Source"
action {
category = "Source"
configuration = {
RepositoryName = "my-app"
}
input_artifacts = []
name = "Source"
namespace = "SourceVariables"
output_artifacts = ["SourceArtifact"]
owner = "AWS"
provider = "ECR"
region = "ap-northeast-1"
role_arn = null
run_order = 1
version = "1"
}
}
stage {
name = "Build"
action {
category = "Build"
configuration = {
ProjectName = "my-app"
}
input_artifacts = ["SourceArtifact"]
name = "Build"
namespace = "BuildVariables"
output_artifacts = ["BuildArtifact"]
owner = "AWS"
provider = "CodeBuild"
region = "ap-northeast-1"
role_arn = null
run_order = 1
version = "1"
}
}
stage {
name = "Deploy"
action {
category = "Deploy"
configuration = {
ClusterName = "my-app"
ServiceName = "my-app"
}
input_artifacts = ["BuildArtifact"]
name = "Deploy"
namespace = "DeployVariables"
output_artifacts = []
owner = "AWS"
provider = "ECS"
region = "ap-northeast-1"
role_arn = null
run_order = 1
version = "1"
}
}
}
好!終於!經過一番無聊的 import,我們終於(應該)把所有 resource import 進 terraform 了!import block 在 import 完成後可以刪掉省得佔空間~