iT邦幫忙

2023 iThome 鐵人賽

DAY 8
0
自我挑戰組

Terraform 繁體中文系列 第 8

Day8-【入門教程】Terraform代碼的書寫—配置語法及輸入變數

  • 分享至 

  • xImage
  •  

原簡體中文教程連結: Introduction.《Terraform入門教程》


1.4.2.1. 配置語法

這裡講的仍然是 HCL 的語法,但我們只講一些關鍵語法。如果讀者有興趣了解完整資訊可以存取 HCL 語法規約

1.4.2.1.1. 參數

參數賦值就是將一個值賦給一個特定的名稱:

image_id = "abc123"

等號前的識別符就是參數名,等號後的表達式就是參數值。參數賦值時 Terraform 會檢查類型是否符合。參數名稱是確定的,參數值可以是確定的字面量硬編碼,也可以是一組表達式,用以透過其他的值加以計算結果值。

1.4.2.1.2. 塊

一個區塊是包含一組其他內容的容器,例如:

resource "aws_instance" "example" {
  ami = "abc123"

  network_interface {
    # ...
  }
}

一個區塊有一個類型(上面的例子裡類型就是 resource)。每個區塊類型都定義了類型關鍵字後面要跟多少標籤,例如 resource 區塊規定了後面要跟兩個標籤-在例子裡就是 aws_instanceexample 。一個區塊類型可以規定任意多個標籤,也可以沒有標籤,例如內嵌的 network_interface 區塊。

在塊類型及其後續標籤之後,就是塊體。塊體必須被包含在一對花括號中間。在區塊體中可以進一步定義各種參數和其他的區塊。

Terraform 規範定義了有限多個頂級區塊類型,也就是可以遊離任何其他區塊獨立定義在設定檔中的區塊。大部分的 Terraform 功能(例如 resource, variable, output, data 等)都是頂級區塊。

1.4.2.1.3. 標識符

參數名稱、區塊類型名稱以及其他 Terraform 規格中定義的結構的名稱,例如 resource、variable 等,都是識別碼。

合法的識別碼可以包含字母、數字、底線 (_) 以及減號 (-)。標識符首字母不可以為數字。

若要了解完整的識別碼規範,請造訪 Unicode 標識符語法

1.4.2.1.4. 註釋

Terraform 支援三種註釋:

  • # 單行註釋,其後的內容為註釋
  • // 單行註釋,其後的內容為註釋
  • /**/,多行註釋,可以註解多行
    預設情況下單行註解優先使用 #。自動化格式整理工具會自動把 // 換成 #。

1.4.2.1.5. 編碼以及換行

Terraform 設定檔必須始終使用 UTF-8 編碼。分隔符號必須使用 ASCII 符號,其他識別碼、註解以及字串字面量均可使用非 ASCII 字元。

Terraform 相容於 Unix 風格的換行符以及 Windows 風格的換行符,但理想狀態下應使用 Unix 風格換行符。


1.4.3.1. 輸入變數

在前面的例子中,我們在程式碼中都是使用字面量硬編碼的,如果我們想要在建立、修改基礎設施時動態傳入一些值呢?比如說在程式碼中定義 Provider 時用變數取代硬編碼的存取密鑰,或是由建立基礎架構的使用者來決定建立什麼樣尺寸的主機?我們需要的是輸入變數。

如果我們把一組 Terraform 程式碼想像成一個函數,那麼輸入變數就是函數的入參。輸入變數以 variable 區塊進行定義:

variable "image_id" {
  type = string
}

variable "availability_zone_names" {
  type    = list(string)
  default = ["us-west-1a"]
}

variable "docker_ports" {
  type = list(object({
    internal = number
    external = number
    protocol = string
  }))
  default = [
    {
      internal = 8300
      external = 8300
      protocol = "tcp"
    }
  ]
}

這些都是合法的輸入參數定義。緊跟 variable 關鍵字的就是變數名。在一個 Terraform 模組(同一個資料夾中的所有 Terraform 程式碼文件,不包含子資料夾)中變數名稱必須是唯一的。我們在程式碼中可以透過 var.<NAME> 的方式引用變數的值。有一組關鍵字不可以被用來當作輸入變數的名字:

  • source
  • version
  • providers
  • count
  • for_each
  • lifecycle
  • depends_on
  • locals

輸入變數只能在宣告該變數的目錄下的程式碼中使用。
輸入變數區塊中可以定義一些屬性。

1.4.3.1.1. 類型

可以在輸入變數區塊中透過 type 定義類型,例如:

variable "name" {
    type = string
}
variable "ports" {
    type = list(number)
}

定義了類型的輸入變數只能被賦予符合類型約束的值。

1.4.3.1.2. 預設值

預設值定義了當 Terraform 無法獲得一個輸入變數得到值的時候會使用的預設值。例如:

variable "name" {
    type    = string
    default = "John Doe"
}

當 Terraform 無法透過其他途徑獲得 name 的值時,var.name 的值為 "John Doe"

1.4.3.1.3. 描述

可以在輸入變數中定義一個描述,簡單地向呼叫者描述該變數的意義和用法:

variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."
}

如果在執行 terraform plan 或是 terraform apply 時 Terraform 不知道某個輸入變數的值,Terraform 會在命令列介面上提示我們為輸入變數設定一個值。例如上面的輸入變數程式碼,執行 terraform apply 時:

$ terraform apply
var.image_id
  The id of the machine image (AMI) to use for the server.

  Enter a value:

為了使的程式碼的使用者能夠準確地理解輸入變數的意義和用法,我們應該站在使用者而非程式碼維護者的角度編寫輸入變數的描述。描述並不是註解!

1.4.3.1.4. 斷言

輸入變數的斷言是 Terraform 0.13.0 開始引入的新功能,在過去,Terraform 只能用類型約束確保輸入參數的類型是正確的,曾經有不少人試圖透過奇技淫巧來實現更加複雜的變量校驗斷言。如今 Terraform 終於正式加入了相關的功能。

variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."

  validation {
    condition     = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
    error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
  }
}

condition 參數是一個 bool 類型的參數,我們可以用一個表達式來定義如何界定輸入變數是合法的。當 contidion 為 true 時輸入變數合法,反之不合法。condition 表達式中只能透過 var.\ 引用目前定義的變量,並且它的計算不能產生錯誤。

假如表達式的計算產生一個錯誤是輸入變數驗證的一種判定手段,那麼可以使用 can 函數來判定表達式的執行是否拋錯。例如:

variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."

  validation {
    # regex(...) fails if it cannot find a match
    condition     = can(regex("^ami-", var.image_id))
    error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
  }
}

在上述例子中,如果輸入的 image_id 不符合正規表示式的要求,那麼 regex 函數呼叫就會拋出一個錯誤,這個錯誤就會被 can 函數捕獲,輸出 false

condition 表達式如果為 false,Terraform 會傳 error_message 回中定義的錯誤訊息。error_message 應該完整描述輸入變數校驗失敗的原因,以及輸入變數的合法約束條件。

1.4.3.1.5. 在命令列輸出中隱藏值

此功能於 Terraofrm v0.14.0 開始引入。

將變數設為 sensitive 可以防止我們在設定檔中使用變數時 Terraform 在 planapply 指令的輸出中展示與變數相關的值。

Terraform 仍然會將敏感資料記錄在狀態檔案中,任何可以存取狀態檔案的人都可以讀取到明文的敏感資料值。

宣告一個變數包含敏感資料值需要將 sensitive 參數設定為 true

variable "user_information" {
  type = object({
    name    = string
    address = string
  })
  sensitive = true
}

resource "some_resource" "a" {
  name    = var.user_information.name
  address = var.user_information.address
}

任何使用了敏感變數的表達式都將被視為敏感的,因此在上面的範例中, resource “some_resource” “a” 的兩個參數也將在計劃輸出中被隱藏:

Terraform will perform the following actions:

  # some_resource.a will be created
  + resource "some_resource" "a" {
      + name    = (sensitive)
      + address = (sensitive)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

在某些情況下,我們會在巢狀區塊中使用敏感變量,Terraform 可能會將整個區塊視為敏感的。這發生在那些包含有要求值是唯一的內嵌區塊的資源中,公開這種內嵌區塊的部分內容可能會暗示兄弟區塊的內容。

 # some_resource.a will be updated in-place
  ~ resource "some_resource" "a" {
      ~ nested_block {
          # At least one attribute in this block is (or was) sensitive,
          # so its contents will not be displayed.
        }
    }

Provider 還可以將資源屬性宣告為敏感屬性,這將導致 Terraform 將其從常規輸出中隱藏。

如果打算使用敏感值作為輸出值的一部分,Terraform 會要求您將輸出值本身標記為敏感值,以確認確實打算將其匯出。

1.4.3.1.5.1. Terraform 可能暴露敏感變數的情況

sensitive 變數是一個以設定檔為中心的概念,值會毫無混淆地傳送給 Provider。如果該值包含在錯誤訊息中,則 Provider 報錯時可能會暴露該值。例如,即使「foo」 是敏感值,Provider 也可能傳回下列錯誤:"Invalid value 'foo' for field"

如果將資源屬性用作、或是作為 Provider 定義的資源 ID 的一部分,則 apply 將公開該值。在下面的範例中,前綴屬性已設定為 sensitive 變量,但隨後該值(“jae”)作為資源ID 的一部分公開:

  # random_pet.animal will be created
  + resource "random_pet" "animal" {
      + id        = (known after apply)
      + length    = 2
      + prefix    = (sensitive)
      + separator = "-"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

...

random_pet.animal: Creating...
random_pet.animal: Creation complete after 0s [id=jae-known-mongoose]

1.4.3.1.6. 禁止輸入變數為空

此功能自 Terraform v1.1.0 開始被引入

輸入變數的 nullable 參數控制模組呼叫者是否可以將 null 指派給變數。

variable "example" {
  type     = string
  nullable = false 
}

nullable 的預設值為 true。當 nullabletrue 時,null 是變數的有效值,且模組程式碼必須始終考慮變數值為 null 的可能性。將 null 作為模組輸入參數傳遞將覆蓋輸入變數上定義的預設值。

nullable 設為 false 可確保變數值在模組內永遠不會為空。如果 nullablefalse 且輸入變數定義有預設值,則當模組輸入參數為 null 時,Terraform 將使用預設值。

nullable 參數僅控制變數的直接值可能為 null 的情況。對於集合或物件類型的變量,例如列表或對象,呼叫者仍然可以在集合元素或屬性中使用null,只要集合或物件本身不為 null

1.4.3.1.7. 對輸入變數賦值

1.4.3.1.7.1. 命令列參數

對輸入變數賦值有幾種途徑,一種是在呼叫 terraform plan 或是 terraform apply 指令時以參數的形式傳入:

$ terraform apply -var="image_id=ami-abc123"
$ terraform apply -var='image_id_list=["ami-abc123","ami-def456"]'
$ terraform plan -var='image_id_map={"us-east-1":"ami-abc123","us-east-2":"ami-def456"}'

可以在一條指令中使用多個 -var 參數。

1.4.3.1.7.2. 參數文件

第二種方法是使用參數檔。參數檔的後綴名可以是 .tfvars 或是 .tfvars.json.tfvars 檔案使用 HCL 語法,.tfvars.json 使用 JSON 語法。

.tfvars 為例,參數檔中以 HCL 程式碼對需要賦值的參數進行賦值,例如:

image_id = "ami-abc123"
availability_zone_names = [
  "us-east-1a",
  "us-west-1c",
]

後綴名為 .tfvars.json 的檔案用一個 JSON 物件來對輸入變數賦值,例如:

{
  "image_id": "ami-abc123",
  "availability_zone_names": ["us-west-1a", "us-west-1c"]
}

呼叫 terraform 指令時,透過 -var-file 參數指定要使用的參數文件,例如:

terraform apply -var-file="testing.tfvars"
terraform apply -var-file="testing.tfvars.json"

有兩種情況,你無需指定參數檔:

  • 目前模組內有名為 terraform.tfvars 或是 terraform.tfvars.json 的文件
  • 目前模組內有一個或多個後綴名為 .auto.tfvars 或是 .auto.tfvars.json 的文件
    Terraform 會自動使用這兩種自動參數檔對輸入參數賦值。

1.4.3.1.7.3. 環境變量

可以透過設定名為 TF_VAR_<NAME> 的環境變數為輸入變數賦值,例如:

$ export TF_VAR_image_id=ami-abc123
$ terraform plan
...

在環境變數名稱大小寫敏感的作業系統上,Terraform 要求環境變數中的 \ 與 Terraform 程式碼中定義的輸入變數名稱大小寫完全一致。

環境變數傳值非常適合在自動化管線中使用,尤其適合用來傳遞敏感數據,類似密碼、存取金鑰等。

1.4.3.1.7.4. 互動介面傳值

在前面介紹斷言的例子中我們看到過,當我們從命令列介面執行 terraform 操作,Terraform 無法透過其他途徑取得一個輸入變數的值,而該變數也沒有定義預設值時,Terraform 會進行最後的嘗試,在互動介面上要求我們給出變數值。

1.4.3.1.8. 輸入變數賦值優先權

當上述的賦值方式同時存在時,同一個變數可能會被賦值多次。Terraform 會使用新值覆蓋舊值。

Terraform 載入變數值的順序是:

  1. 環境變數
  2. terraform.tfvars 文件(如果存在的話)
  3. terraform.tfvars.json 文件(如果存在的話)
  4. 所有的 .auto.tfvars.auto.tfvars.json 文件,以字母順序排序處理
  5. 透過 -var 或是 -var-file 命令列參數傳遞的輸入變量,按照命令列參數中定義的順序加載

假如以上方式皆未能成功對變數賦值,那麼 Terraform 會嘗試使用預設值;對於沒有定義預設值的變量,Terraform 會採用互動介面方式要求使用者輸入一個。對於某些 Terraform 指令,如果執行時帶有 -input=false 參數停用了互動介面傳值方式,那麼就會報錯。

1.4.3.1.9. 複雜型別傳值

透過參數檔傳值時,可以直接使用 HCL 或 JSON 語法對複雜型別傳值,例如 list 或 map。

對於某些場景下必須使用 -var 命令列參數,或是環境變數傳值時,可以用單引號引用 HCL 語法的字面量來定義複雜類型,例如:

export TF_VAR_availability_zone_names='["us-west-1b","us-west-1d"]'

由於採用這種方法需要手動處理引號的轉義,所以這種方法比較容易出錯,複雜類型的傳值建議盡量通過參數檔。


原簡體中文教程連結: Introduction.《Terraform入門教程》


上一篇
Day7-【入門教程】Terraform代碼的書寫及類型
下一篇
Day9-【入門教程】Terraform代碼的書寫—輸出值及局部值
系列文
Terraform 繁體中文25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言