業務でTerraformを使う機会が増えて理解が足りていない部分があったので、理解を深めるために今回はAzureストレージをTerraformで構築しながらまとめていきます。
Terraformとは
- HashiCorp社によって開発されたインフラの設定をコードで管理するためのIaC(Infrastructure as Code)ツール
- HCL(HashiCorp Configration Language)という言語で定義を記述しインフラ構築をおこなうことができる
- AWSやMicrosoft Azure、Google Cloud Plattformなど様々なパブリッククラウドに対応
tfstateファイル
Terraformで管理しているリソースの現状の構成を記録するためのファイルになります。
定義したリソースの適用(apply)時に適用した構成が記録されます。
ファイルの管理は以下の2つの管理方法があります。
- ローカル管理
- 特に保存先を指定していなければデフォルトでローカル環境のterraformを実行したカレントディレクトリにtfstateファイルが生成される
- バックエンド管理
- クラウドのストレージ上で管理をおこない開発メンバー間の共有ができる
- バージョニング管理ができ、何かあった時の復旧が容易
TerraformでAzureのStrage Accountを構築してみる
事前準備
以下のチュートリアルを参考にTerraformのインストールとAzureに対しての認証をおこなっておきます。
また、以下を参考にtfstateファイルをストレージで管理できるようにストレージアカウントとコンテナーを作成しておきます。
構成ファイルの作成
provider.tf
まずは使用するプロバイダーの定義をterraform
ブロック内に記述していきます。
今回はstateファイルをAzureストレージで管理するため、backend
ブロックにストレージの情報も記述します。
terraform {
required_providers {
azurerm = {
version = ">=3.49.0"
}
}
backend "azurerm" {
resource_group_name = "backend_rg" # 事前準備で作成した resource-group-name
storage_account_name = "xxxxxtfbackend01" # 事前準備で作成した storage-account-name
container_name = "backend" # 事前準備で作成した container-name
key = "terraform.tfstate"
}
}
provider "azurerm" {
features {}
}
variables.tf
次に変数の定義を記述していきます。
variable "location" {
type = string
default = "japaneast"
}
variable "allow_nested_items_to_be_public" {
type = bool
default = false
}
variable "container_access_type" {
type = string
default = "private"
}
type
に変数の値の型、default
にデフォルト値を定義しておきます。
定義したvariableはvar.変数名
という形で参照することができます。
main.tf
作成するリソースの定義を記述していきます。
今回はリソースグループとストレージカウント、ストレージコンテナを構築していきます。
# リソースグループ
resource "azurerm_resource_group" "rg" {
name = "rg_strage"
location = var.location
}
# ストレージアカウント
resource "azurerm_storage_account" "sa" {
name = "xxxxxtf39demo01"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
account_tier = "Standard"
account_replication_type = "LRS"
allow_nested_items_to_be_public = var.allow_nested_items_to_be_public
}
# ストレージコンテナー
resource "azurerm_storage_container" "sc" {
name = "stragecon"
storage_account_name = azurerm_storage_account.sa.name
container_access_type = var.container_access_type
}
リソースの構築
初期化
init
コマンドを実行して作業ディレクトリの初期化やプラグインのダウンロードをおこないます。
$ terraform init
Initializing the backend...
Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Finding hashicorp/azurerm versions matching ">= 3.49.0"...
- Installing hashicorp/azurerm v3.53.0...
- Installed hashicorp/azurerm v3.53.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
初期化が成功するとTerraform has been successfully initialized!
のメッセージが表示されます。
-backend-config
オプションを利用して環境ごとに定義したbackendを指定することも可能です。
初期化をおこなうとローカルに以下のディレクトリとファイルが生成されます。
- .terraform
- 初期化時にキャッシュしたプロバイダーやモジュールなどの情報を格納するディレクトリ
- .terraform.lock.hcl
- 初期化時にキャッシュしたプロバイダーやモジュールのバージョンの依存関係をロックするためのファイル
実行計画の確認
初期化が終わったので、plan
コマンドを実行してapply
する前に実行計画の確認をしていきます。
$ terraform plan
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:
# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "rg_strage"
}
# azurerm_storage_account.sa will be created
+ resource "azurerm_storage_account" "sa" {
+ access_tier = (known after apply)
+ account_kind = "StorageV2"
+ account_replication_type = "LRS"
+ account_tier = "Standard"
+ allow_nested_items_to_be_public = false
+ cross_tenant_replication_enabled = true
+ default_to_oauth_authentication = false
+ enable_https_traffic_only = true
+ id = (known after apply)
+ infrastructure_encryption_enabled = false
+ is_hns_enabled = false
+ large_file_share_enabled = (known after apply)
+ location = "japaneast"
+ min_tls_version = "TLS1_2"
+ name = "xxxxxtf39demo01"
+ nfsv3_enabled = false
+ primary_access_key = (sensitive value)
+ primary_blob_connection_string = (sensitive value)
+ primary_blob_endpoint = (known after apply)
+ primary_blob_host = (known after apply)
+ primary_connection_string = (sensitive value)
+ primary_dfs_endpoint = (known after apply)
+ primary_dfs_host = (known after apply)
+ primary_file_endpoint = (known after apply)
+ primary_file_host = (known after apply)
+ primary_location = (known after apply)
+ primary_queue_endpoint = (known after apply)
+ primary_queue_host = (known after apply)
+ primary_table_endpoint = (known after apply)
+ primary_table_host = (known after apply)
+ primary_web_endpoint = (known after apply)
+ primary_web_host = (known after apply)
+ public_network_access_enabled = true
+ queue_encryption_key_type = "Service"
+ resource_group_name = "rg_strage"
+ secondary_access_key = (sensitive value)
+ secondary_blob_connection_string = (sensitive value)
+ secondary_blob_endpoint = (known after apply)
+ secondary_blob_host = (known after apply)
+ secondary_connection_string = (sensitive value)
+ secondary_dfs_endpoint = (known after apply)
+ secondary_dfs_host = (known after apply)
+ secondary_file_endpoint = (known after apply)
+ secondary_file_host = (known after apply)
+ secondary_location = (known after apply)
+ secondary_queue_endpoint = (known after apply)
+ secondary_queue_host = (known after apply)
+ secondary_table_endpoint = (known after apply)
+ secondary_table_host = (known after apply)
+ secondary_web_endpoint = (known after apply)
+ secondary_web_host = (known after apply)
+ sftp_enabled = false
+ shared_access_key_enabled = true
+ table_encryption_key_type = "Service"
}
# azurerm_storage_container.sc will be created
+ resource "azurerm_storage_container" "sc" {
+ container_access_type = "private"
+ has_immutability_policy = (known after apply)
+ has_legal_hold = (known after apply)
+ id = (known after apply)
+ metadata = (known after apply)
+ name = "stragecon"
+ resource_manager_id = (known after apply)
+ storage_account_name = "xxxxxtf39demo01"
}
Plan: 3 to add, 0 to change, 0 to destroy.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run
"terraform apply" now.
plan
コマンドはtfstate
ファイルから現在のリソースの状態を参照し、リソースの追加・変更・削除の差分を表示してくれます。
コマンドの実行が成功したらPlan 1 to add, 0 to change, 0 to destroy
のように追加・変更・削除の計画サマリが表示されます。
今回は初回の実行でTerraformからまだ何もリソースが作成されていないので、3つのリソースが追加として計画サマリが表示されています。
もし現在のリソースの状態と比較しリソースの追加や変更などがなければNo changes.
と表示されます。
このあと実行するapply
コマンドでも同様ですが、以下のようなオプションもあります。
-
-var-file
オプションで.tfvars
の拡張子のファイルを指定して環境ごとの変数定義ファイルを指定することも可能 -
-target
オプションを使用して特定moduleのみ指定することも可能
適用
$ terraform apply
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:
# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "rg_strage"
}
# azurerm_storage_account.sa will be created
+ resource "azurerm_storage_account" "sa" {
+ access_tier = (known after apply)
+ account_kind = "StorageV2"
+ account_replication_type = "LRS"
+ account_tier = "Standard"
+ allow_nested_items_to_be_public = false
+ cross_tenant_replication_enabled = true
+ default_to_oauth_authentication = false
+ enable_https_traffic_only = true
+ id = (known after apply)
+ infrastructure_encryption_enabled = false
+ is_hns_enabled = false
+ large_file_share_enabled = (known after apply)
+ location = "japaneast"
+ min_tls_version = "TLS1_2"
+ name = "xxxxxtf39demo01"
+ nfsv3_enabled = false
+ primary_access_key = (sensitive value)
+ primary_blob_connection_string = (sensitive value)
+ primary_blob_endpoint = (known after apply)
+ primary_blob_host = (known after apply)
+ primary_connection_string = (sensitive value)
+ primary_dfs_endpoint = (known after apply)
+ primary_dfs_host = (known after apply)
+ primary_file_endpoint = (known after apply)
+ primary_file_host = (known after apply)
+ primary_location = (known after apply)
+ primary_queue_endpoint = (known after apply)
+ primary_queue_host = (known after apply)
+ primary_table_endpoint = (known after apply)
+ primary_table_host = (known after apply)
+ primary_web_endpoint = (known after apply)
+ primary_web_host = (known after apply)
+ public_network_access_enabled = true
+ queue_encryption_key_type = "Service"
+ resource_group_name = "rg_strage"
+ secondary_access_key = (sensitive value)
+ secondary_blob_connection_string = (sensitive value)
+ secondary_blob_endpoint = (known after apply)
+ secondary_blob_host = (known after apply)
+ secondary_connection_string = (sensitive value)
+ secondary_dfs_endpoint = (known after apply)
+ secondary_dfs_host = (known after apply)
+ secondary_file_endpoint = (known after apply)
+ secondary_file_host = (known after apply)
+ secondary_location = (known after apply)
+ secondary_queue_endpoint = (known after apply)
+ secondary_queue_host = (known after apply)
+ secondary_table_endpoint = (known after apply)
+ secondary_table_host = (known after apply)
+ secondary_web_endpoint = (known after apply)
+ secondary_web_host = (known after apply)
+ sftp_enabled = false
+ shared_access_key_enabled = true
+ table_encryption_key_type = "Service"
}
# azurerm_storage_container.sc will be created
+ resource "azurerm_storage_container" "sc" {
+ container_access_type = "private"
+ has_immutability_policy = (known after apply)
+ has_legal_hold = (known after apply)
+ id = (known after apply)
+ metadata = (known after apply)
+ name = "stragecon"
+ resource_manager_id = (known after apply)
+ storage_account_name = "xxxxxtf39demo01"
}
Plan: 3 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
azurerm_resource_group.rg: Creating...
azurerm_resource_group.rg: Creation complete after 2s [id=/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_strage]
azurerm_storage_account.sa: Creating...
azurerm_storage_account.sa: Still creating... [10s elapsed]
azurerm_storage_account.sa: Still creating... [21s elapsed]
azurerm_storage_account.sa: Still creating... [31s elapsed]
azurerm_storage_account.sa: Still creating... [41s elapsed]
azurerm_storage_account.sa: Still creating... [51s elapsed]
azurerm_storage_account.sa: Still creating... [1m1s elapsed]
azurerm_storage_account.sa: Still creating... [1m11s elapsed]
azurerm_storage_account.sa: Creation complete after 1m19s [id=/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_strage/providers/Microsoft.Storage/storageAccounts/xxxxxtf39demo01]
azurerm_storage_container.sc: Creating...
azurerm_storage_container.sc: Creation complete after 0s [id=https://xxxxxtf39demo01.blob.core.windows.net/stragecon]
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
apply
コマンドは定義したインフラ構成をクラウドに適用するコマンドです。
コマンド実行後に再度plan
結果が表示され、Enter a Value:
でyes
と入力すると適用されます。
apply
が完了するとterraform.tfstateファイルに適用したリソースの情報が記録されます。
ではリソースが適用できたか再度plan
コマンドを実行して確認してみます。
terraform plan
azurerm_resource_group.rg: Refreshing state... [id=/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_strage]
azurerm_storage_account.sa: Refreshing state... [id=/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_strage/providers/Microsoft.Storage/storageAccounts/xxxxxtf39demo01]
azurerm_storage_container.sc: Refreshing state... [id=https://xxxxxtf39demo01.blob.core.windows.net/stragecon]
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
No changes. Your infrastructure matches the configuration.
となったので差分がない状態です。
Azureポータルからも確認してリソースが作成されていることが確認できました。
リソースの追加ができたので、今度は別のコンテナーのリソース定義をmain.tf
ファイルに追加してplan
実行してみます。
・・・
resource "azurerm_storage_container" "sc02" {
name = "stragecon02"
storage_account_name = azurerm_storage_account.sa.name
container_access_type = var.container_access_type
}
Plan実行結果
$ terraform plan
azurerm_resource_group.rg: Refreshing state... [id=/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_strage]
azurerm_storage_account.sa: Refreshing state... [id=/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_strage/providers/Microsoft.Storage/storageAccounts/xxxxxtf39demo01]
azurerm_storage_container.sc: Refreshing state... [id=https://xxxxxtf39demo01.blob.core.windows.net/stragecon]
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:
# azurerm_storage_container.sc02 will be created
+ resource "azurerm_storage_container" "sc02" {
+ container_access_type = "private"
+ has_immutability_policy = (known after apply)
+ has_legal_hold = (known after apply)
+ id = (known after apply)
+ metadata = (known after apply)
+ name = "stragecon02"
+ resource_manager_id = (known after apply)
+ storage_account_name = "xxxxxtf39demo01"
}
Plan: 1 to add, 0 to change, 0 to destroy.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run
"terraform apply" now.
先程apply
した3つのリソースはtfstate
ファイルに記録されているため、planの差分として出ません。
今回、定義に追加したコンテナーが1つ追加として差分が出ます。
他の主要なコマンドやブロック構文まとめ
コマンド
format
$ terraform fmt
- Terraformの構成ファイルのインデントを揃えるためのフォーマットをおこなうコマンド
validate
$ terraform validate
- 定義した構文のチェックを行うコマンド
-
terraform plan
コマンドでもvalidateコマンドの機能が内包されている
destroy
$ terraform destoroy
- Terraformで管理しているリソースの削除コマンド
-
terraform plan -destroy
コマンドで削除対象のリソースの一覧を確認することも可能
state
$ terraform state list
- 作成したリソースの一覧を確認するためのコマンド
$ terraform state show リソース
- リソースを指定して作成したリソースの詳細を確認するためのコマンド
ブロック
DataSource
-
data
ブロックで定義をおこない、Terraformの外部で定義された情報を参照するために使用
data "azurerm_storage_container" "sc" {
name = "stragecon"
storage_account_name = "xxxxxtf39demo01"
}
Output Values
-
output
ブロックで定義をおこない、リソースの情報を出力するために使用 -
apply
コマンド実行すると出力される
data "azurerm_storage_container" "sc" {
name = "stragecon"
storage_account_name = "xxxxxtf39demo01"
}
output "azurerm_storage_container_access_type" {
value = data.azurerm_storage_container.sc.container_access_type
}
apply
コマンド実行後にcontainer_access_typeが出力できています。
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
azurerm_storage_container_access_type = "private"
参考
最後に
今回はすごくシンプルな構成でしたが、実際にTerraformで構築しながら基礎の部分の理解を深めていきました。
今後も実際に構築しながら色々と試していきたいと思います。