Day 18 Terraform variable & 驗證 configuration
import 完 resource 需要 resource 們是否正確及完整,否則佈建另一個環境(例如正式環境)可能才發現 configuration 是無法運作的。那要怎麼驗證呢?可以到另一個 AWS region 進行 terraform apply
把 resource 建立上去,檢查是不是一切正常。(本日程式碼)
Terraform 的變數(variable)
要用剛寫好的 configuration 到另一個 region 佈建,第一件要做的事情是:把所有 import 過程產生的 region 名稱 ap-northeast-1
變成變數。(不然…要…怎麼…換 region… 😶)
這邊介紹一個新的 block:variable block。它長得像這樣:
1 | variable "[NAME]" { |
接在 variable
關鍵字後面的是變數名稱,type
指定變數是字串或者數字或者 boolean 等等型態,不指定也沒關係。default
是這個變數的預設值,也就是如果沒有給值,它就會使用的值。假如變數沒有給預設值,terraform 在 apply 跟 plan 的時候會要求給變數值。
給值的方式有兩種,一個是用 .tfvars
檔案配合參數 -var-file
指定,或者不用參數指定、直接使用預設檔案 terraform.tfvars
或 terraform.tfvars.json
。如果 terraform 沒找到 terraform.tfvars
,也沒有用 -var-file
指定,變數又沒有預設值的話,plan 跟 apply 的時候會跳訊息要求輸入變數值:
如果變數多……都是直接用檔案指定啦……一個個手 key……嗯… 😨
把值抽(extract)成變數之後,使用變數的地方用 var.[NAME]
來 reference 變數。我們通常會把 variable block 集中放在 variables.tf
這個檔案裡。(註:筆者習慣用 extract variable 這個詞,寫成中文就是「抽變數」)
下面來看看實際操作~
新增 variables.tf
檔案,在裡面加入 region 變數:
1 | variable "region" { |
要使用變數的地方改成:
1 | region = var.region |
如果字串裡用到變數,要用 ${}
把 var
跟變數名稱包起來,像這樣:
1 | name = "CodeBuildBasePolicy-my-app-${var.region}" |
name
最後的值會是 CodeBuildBasePolicy-my-app-
加上 region
變數展開的值。
在 terraform.tfvars
裡指定變數值:
1 | region = "ap-northeast-1" |
這樣 apply 跟 plan 的時候 region
變數的值就是 ap-northeast-1
。
前面說到 -var-file
可以指定不同的 .tfvars
檔案,還有 Day 6 說到 stage 跟 production 環境幾乎一樣、差別在機器與資源的規模比較小。所以我們可以為不同環境各自建立 .tfvars
檔案(像是 stage.tfvars
跟 prod.tfvars
),裡面設定各個環境所需要的參數(EC2 instance type、task desired count 等等),然後用 -var-file
做 plan 跟 apply,就能用同一份 configuration 對多個環境進行佈建了!
利用變數我們可以透過一份 configuration 建立多個環境,避免手動操作或者使用多份 configuration 造成環境有所差異,這對以「跟正式環境相同,只是規模較小,好能夠在類似正式的環境中進行測試」為目的的 stage 環境是很重要的~
把 region 抽成變數後,我們到另一個 region 用 terraform 建立 infrastructure 吧!
到首爾建立 resource 以驗證 configuration 的正確性
開始前,我們要先做個小小的手腳。因為現在儲存 state 的 backend 依然是 local,也就是本地檔案,它們記錄著東京 region 的 resource,可以用 terraform state list
列出目前的 resource 們。我們無法在這個情況下到另一個 region 建立 resource,因為 state 覺得你已經建好了,除非多寫 resource block 才會有新的 resource(但這樣不對啊…)。我們這邊簡單處理:建立一個新的資料夾,把 terraform.tfstate
跟 terraform.tfstate.backend
移進去(有興趣的朋友可以嘗試使用 workspace 功能):
1 | $ mkdir origin-state |
這樣我們又得到一個乾淨的 state 了~
接下來我們把 terraform.tfvars
裡的 region 變數值改成 ap-northeast-2
(首爾),接著 terraform apply
~
然後遇到一堆 error,兵來將擋水來土淹,來 error 就解吧~~
IAM Role 相關 error
這兩個是說 IAM role 已經存在,不能再建立相同名稱的 role。雖然我們把 resource 建立在 ap-northeast-2
region,但 IAM role 是 global level 的、所有 region 都是用同樣的 IAM role 們,我們當然不能再建立同樣名稱的 role。
簡單解法:把名字抽成變數後指定另一個名稱。
可能會想問為什麼不是把 resource 拿掉或者 import 現有的 IAM role 而是換名稱?因為我們現在是在驗證整份 configuration 的正確與完整性,它需要擁有在一個全新 AWS account 環境中建立起我們要的 resource 的能力,我們要模擬這個狀況才算是驗證。真正使用如果是在相同 AWS account 下建立 resource,依據情境有可能會希望共用 resource,那時候再用 import 或其他方式處理。
抽出兩個 variable:
1 | variable "codebuild_project_role_name" { |
並且在 terraform.tfvars
指定:
1 | codebuild_project_role_name = "codebuild-my-app-service-role-ap-northeast-2" |
resource 設定名稱的地方也要改用 var.codebuild_project_role_name
跟 var.ec2_inst_role_name
。
接下來跟前面 IAM role 也是相同問題――同樣名稱的 instance profile 已經存在,我們一樣用變數幫 instance profile 換個名字。
1 | variable "ec2_inst_profile_name" { |
並且指定:
1 | ec2_inst_profile_name = "ecsInstanceRole-ap-northeast-2" |
Security Group 相關 error
不能用 default
這個保留字當作 security group 的名稱,它對應的是我們 aws_security_group.default
resource。這邊要改用 aws_default_security_group
resource 來管理預設 security group:
1 | resource "aws_default_security_group" "default" { |
Subnet 相關 error
這裡它說找不到 vpc,但從 AWS web console 是有看到建立的 vpc 的,那我們來看看這幾個 resource:
1 | resource "aws_subnet" "public_1a" { |
噢,原來是忘記用 vpc resource 指定 vpc_id
,既然看到了就把在 network.tf
所有類似問題通通修正(commit)。
Route Table 相關 error
這個 error 是說我們不能用 terraform 建立 target 是 local 的 route,要用 terraform 管理這種 route 的話要 import。這邊我們改用 terraform import
這個指令來 import resource,這是在 terraform v1.5 以前、沒有 import block 時 import resource 的方式。
參考 aws_route 文件 的 import 部份好知道要以 AWS cloud 上的什麼資訊作為識別來 import:
1 | terraform import aws_route.internal rtb-044d756cdd33184bf_172.16.0.0/16 |
import 正常的話會出現這樣的畫面:
Auto Scaling Group 相關 error
因為 AMI 不是 global level 的,相同 AMI 在不同 region 會有不同 id,所以我們先在東京(ap-northeast-1)找到 ami-0f386dc4885ec169f
AMI 的名稱是 amzn2-ami-ecs-hvm-2.0.20230606-x86_64-ebs
。接著到首爾(ap-northeast-2
)用名稱找到對應的 AMI id 是 ami-0063312c13bc1e1ad
。一樣,把 launch template 內指定 ami id 的參數值抽成變數,然後在 terraform.tfvars
指定 ami-0063312c13bc1e1ad
。
除了 AMI id 之外,我們也把 launch template 的名稱抽成變數並且指定另一個名稱給首爾使用。
除了 launch template 的 AMI id,也要修正 auto scaling group 寫死的 launch template name:
1 | launch_template { |
改成:
1 | launch_template { |
接著 error 就變成了:
因為 key pair 也不是跨 region 的,所以我們要在首爾 region 建立一個 key pair。名稱可以跟東京的相同也可以不同,如果名稱不同就要改用變數表示 key pair 名稱。
ALB 相關 error
我們不能同時在 ALB 指定 subnet_mapping
跟 subnets
,刪掉其中一個吧~
ECS 相關 error
我們在 ECS service resource 裡直接寫死 task definition 的名稱,改成:
1 | task_definition = aws_ecs_task_definition.td.family |
CodePipeline 相關 error
看起來我們前面 import resource 的時候少 import codepipeline 需要的 s3 bucket(所以說要驗證…真的會漏啊…),這邊我們直接加上 s3 bucket resource:
1 | resource "aws_s3_bucket" "codepipeline_artifact" { |
並且修正 aws_codepipeline.pipeline
的 artifact_store
:
1 | artifact_store { |
好~!總算把 error 都解完、順利 apply 到首爾 region 了!