原簡體中文教程連結: Introduction.《Terraform入門教程》
一般來說 Terraform 會載入模組內所有的 .tf
和 .tf.json
文件,並要求文件內定義了一組無重複的物件。如果兩個檔案嘗試定義同一個對象,那麼 Terraform 會報錯。
在某些少見場景中,能夠用單獨的檔案重載已有物件配置的特定部分將會十分有用。比如說,由工程師編寫的設定檔能夠在執行時被程式產生的 JSON 檔案部分重載。
為支援這些少見場景,Terrform 會對後綴名為 override.tf
和 override.tf.json
的程式碼檔案進行特殊處理。對於名為 override.tf
和 override.tf.json
的程式碼檔案也會進行相同的特殊處理。
Terraform 一開始載入程式碼檔案時會跳過這些重載文件,然後才會依照字典順序一個一個處理重載文件。對重載檔案中定義的所有頂級區塊(resource、data 等),Terraform 會嘗試尋找對應的已有物件並且將重載內容合併進已有物件。
重載檔案只應使用於特殊場景,過度使用會使得讀者在閱讀原始程式碼檔案時被迫還要閱讀所有的重載檔案才能理解物件配置,從而降低了程式碼的可讀性。使用重載文件時,請在原始文件被重載的部分中添加相應註釋,提醒未來的讀者哪些部分會被重載文件修改。
如果我們有一個名為 example.tf
的程式碼檔案:
resource "aws_instance" "web" {
instance_type = "t2.micro"
ami = "ami-408c7f28"
}
然後我們建立一個名為 override.tf
的檔案:
resource "aws_instance" "web" {
ami = "foo"
}
Terraform 隨後會合併兩者,實際的配置會是這樣的:
resource "aws_instance" "web" {
instance_type = "t2.micro"
ami = "foo"
}
不同的區塊類型有著微不同的合併行為,某些特定區塊內的特殊構造會以特殊形式被合併。
一般來說:
如果有多個重載檔案定義了同一個頂級區塊,那麼重載效果是疊加的,後載入的重載區塊會在先前載入的重載區塊生效的基礎上合併。重載操作首先依照檔案名稱的字典序其次是在重載檔案中的位置決定執行順序。
有一些針對特定頂級區塊類型的特殊合併行為規則,我們將重載檔案中定義的區塊稱為重載區塊,重載區塊在普通檔案中對應的區塊稱為來源區塊:
在 resource
區塊內,所有 lifecycle
區塊的內容會依照參數逐條合併。比如說,一個重載塊只定義了 create_before_destroy
參數而源塊定義了 ignore_changes
,那麼 create_before_destroy
被合併的同時 igonore_changes
將會被保留。
如果重載的 resource
區塊包含了一個或多個 provisioner
,那麼來源區塊內所有的 provisioner
會被忽略。
如果重載的 resource
區塊內包含了一個 connection
區塊,那麼它將完全覆蓋所有來源區塊內定義的 connection
區塊
不允許在重載區塊內定義 depends_on
參數,那將會引發一個錯誤。
variable
區塊內參數的合併遵循上述的標準流程,但對於 type
和 default
參數的處理會有一些特殊的考慮。
如果來源區塊定義了 default
值而重載區塊修改了變數的 type
,Terraform 會嘗試將 default
值轉換成新類型,如果轉換失敗則會報錯。
同樣的,如果來源區塊定義了 type
參數而重載區塊修改了 default
值,那麼新的 default
值必須能夠轉換成原先的型別。
不允許在重載區塊內定義 depends_on
參數,這會引發一個錯誤。
所有的 locals
區塊都定義了一個或多個命名值。針對 locals
的合併會是依照命名值的名字逐條執行的,不論命名值是在哪個 locals
區塊內被定義的。
如果重載區塊定義了 required_providers
參數,那麼它的值會被逐條合併,這就允許重載區塊在不影響其他 Provider 的情況下調整單一 Provider 的版本約束。
重載區塊內的 requeired_version
和 required_providers
里的配置完全覆蓋來源區塊內的相應配置。如果來源區塊和重載區塊都定義了 required_version
,那麼來源區塊的配置就會被完全忽略。
Terraform 推薦以下程式碼規格:
ami = "abc123"
instance_type = "t2.micro"
resource "aws_instance" "example" {
count = 2 # meta-argument first
ami = "abc123"
instance_type = "t2.micro"
network_interface {
# ...
}
lifecycle { # meta-argument block last
create_before_destroy = true
}
}
aws_instnace
資源內的 root_block_device
、ebs_block_device
、ephemeral_block_device
內嵌區塊共同構成了描述 AWS 區塊儲存的區塊家族,所以他們可以被混合編寫)。check
區塊是 Terraform 1.5 開始引入的新功能。
過去我們可以在 resource
區塊裡的 lifecycle
區塊中驗證基礎設施的狀態。check
區塊填補了在 terraform apply
後驗證基礎設施狀態這項功能中的一塊空白。
check
區塊允許我們定義在每次 plan
以及 apply
操作後執行的自訂的驗證。check
區塊定義的驗證邏輯是作為 plan
和 apply
操作的最後一步執行的。
你可以定義一個包含本地名稱的 check
區塊,其中可以定義一個有限作用範圍的 data
區塊,以及至少一個的斷言。
下面的範例示範了載入 Terraform 官網並驗證 HTTP 回傳狀態碼為 200
。
check "health_check" {
data "http" "terraform_io" {
url = "https://www.terraform.io"
}
assert {
condition = data.http.terraform_io.status_code == 200
error_message = "${data.http.terraform_io.url} returned an unhealthy status code"
}
}
我們可以在 check
區塊使用任意 Provider 提供的任意資料來源作為一個有限作用範圍的資料來源。
一個 check
區塊可以配一個可選的內嵌(也叫有限作用範圍)資料來源。該 data
區塊和普通的 data
區塊行為類似,但你不能在定義它的 check
區塊以外引用它。另外,如果一個有限作用範圍的資料來源運行時觸發了任意錯誤,這些錯誤將被標記為警告,不會阻止 Terraform 繼續執行操作。
你可以使用有限作用範圍的資料來源在 resource
的 lifecycle
外驗證相關基礎設施片段的狀態。在上面的例子裡,如果 terraform_io
資料來源在載入時發生錯誤,那麼我們將會收到一個警告而不是中斷執行的錯誤。
有限作用域的資料來源支援 depends_on
和 provider
元參數,但不支援 count
或 for_each
元參數。
depends_on
depends_on
元參數配合有限作用域資料來源可以提供非常強大的能力。
假設上述範例中的 Terraform 網站是我們即將用同一目錄下的 Terraform 程式碼部署的,在第一次創建 Plan 時因為網站還沒有被創建,所以驗證會失敗,Terraform 總是會在一開始顯示一條讓人分心的警告訊息。
我們可以為該內嵌資料來源新增 depends_on
來確保該資料來源依賴某項組成基礎架構的必要資源,例如負載平衡器。這樣對該資料來源的檢查結果將保持 known after apply
直到依賴項建立完成。此策略避免了在配置階段產生無意義的警告訊息,直到在 plan
和 apply
操作的合適階段執行檢查。
該策略的一個問題是如果有限作用域資料來源所依賴的資源發生了變化,那麼 check
區塊將返回 known after apply
直到 Terraform 完成了對被依賴資源的更新。在某些情況下,這種行為將會引發一些問題。
我們推薦只有在內嵌資料來源依賴某項資源,但又沒有明確的引用其資料時使用 depends_on
元參數。
我們在 check
區塊中使用 assert
區塊定義自訂的斷言條件。每個 check
區塊必須聲明至少一個或更多的 assert
區塊。每個 assert
區塊都包含了一個 condition
屬性與一個 error_message
屬性。
與其他自訂檢查(variable
中的 validation
以及 lifecycle
中的 precondition
和 postcondition
)不同,assert
的斷言不會影響 Terraform 執行操作。失敗的斷言將以警告訊息的形式輸出而不會中斷後續的操作。這與其他諸如 postcondition
這樣的自訂檢查形成了對比,因為它們的檢查失敗會立即終止後續的 plan
以及 apply
操作,傳回錯誤訊息。
assert
區塊中的斷言條件表達式可以引用同一 check
區塊裡的內嵌資料來源數據,以及同一模組中的任意輸入參數、資源、資料來源、模組的輸出值。
check
塊目前不支援元參數。Terraform 團隊目前正在收集有關此功能的回饋。
check
區塊提供了 Terraform 中最靈活的驗證功能。我們可以在其中引用輸出值、輸入參數、資源以及資料來源的值。我們的確可以使用 check
區塊取代所有其他的自訂條件檢查,但這並不意味著我們應該這麼做。
check
與其他檢查最大的差別在於 check
區塊不會中斷 Terraform 的執行。我們需要將這種非阻塞性的行為特徵計入考量來決定採取何種檢查。
輸出值的 precondition
以及輸入變數的 validation
都可以對輸入輸出值進行斷言。
這些檢查是用來阻止 Terraform 在資料有問題時繼續執行的。
舉例來說,如果輸入參數的值是無效的那麼任由 Terraform 執行整個配置文件並沒有什麼意義,這種情況下,check
塊只會輸出有關無效輸入參數的警告,不會打斷 Terraform 的執行,而 validation
區塊則會警告輸入參數值非法,並終止 Terraform 執行 plan
或 apply
操作。
check
塊與 precondition
和 postcondition
的差異更加微妙。
precondition
是自訂條件檢查中最特殊的,因為它們是在資源的變更被計算或應用之前執行的檢查。決定使用 precondition
還是 postcondition
的考量也適用於選擇要使用 precondition
還是 check
區塊。
我們可以在 postcondition
與 check
區塊之間互換來驗證資源和資料來源。例如,我們可以把上述範例中的 check
區塊改寫成 postcondition
,以下的 postcondition
區塊將會驗證對 Terraform 網站的請求是否回傳了狀態碼 200
:
data "http" "terraform_io" {
url = "https://www.terraform.io"
lifecycle {
postcondition {
condition = self.status_code == 200
error_message = "${self.url} returned an unhealthy status code"
}
}
}
check
和 postcondition
區塊都在 plan
或 apply
操作中驗證了 Terraform 網站是否回傳 200
狀態碼,它們的差異是發生錯誤時的行為。
如果是 postcondition
失敗,那麼就無法繼續執行。Terraform 會阻止任意後續的 plan
或 apply
操作。
我們建議使用 check
區塊來驗證基礎設施的整體狀態,僅在希望確保單一資源狀態符合預期時才使用 postcondition
。
原簡體中文教程連結: Introduction.《Terraform入門教程》