[小ネタ] WindowsでTerraformを試してみた

2019.09.09

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

しばたです。
クラスメソッドに入社して以来環境構築にはCloudFormationを使ってきたのですが、CloudFormationにもそこそこ慣れ「そろそろTerraformに手を出そう」と思ったので試してみました。

Terraform入門系のブログ記事は既に腐るほどあるので本記事で得られるものはさほど無いかもしれませんが軽い小ネタだと思ってご覧ください。
本記事ではWindows環境へのTerraformのインストールと簡単な環境構築までを行っていきます。

【追記】TerraformUtil

主にWindows向けにTerraform周りの環境構築(tfenv相当の機能やtflintのインストールなど)を担うツールを自作しました。

このPowerShellモジュールを使ってインストールを簡易に行うことも可能です。

検証環境

検証環境には私の開発機である64bit版 Windows 10 May 2019 Update(1903)を使います。
またテキストエディタにはVisual Studio Code + Terraform拡張を使用しています。
(Visual Studio Code周りの構築手順には触れません)

インストール

Windows版のTerraformはZipアーカイブ中に単一の実行ファイルであるterraform.exeがあり、このファイルを展開すればインストール完了です。
PowerShell(PowerShell Coreでも可)からだと以下の様な感じでインストールおよびPATH環境変数の設定までを行えます。

# 現時点での最新版である Ver.0.12.8 をインストール
$uri = 'https://releases.hashicorp.com/terraform/0.12.8/terraform_0.12.8_windows_amd64.zip'
$outPath = Join-Path $env:TEMP 'terraform_0.12.8_windows_amd64.zip'
$destPath = 'C:\hashicorp\terraform'

# ZIPファイルのダウンロード
Invoke-WebRequest -Uri $uri -OutFile $outPath

# ZIP展開+PATH環境変数の更新
Expand-Archive -Path $outPath -DestinationPath $destPath
[Environment]::SetEnvironmentVariable('PATH', [Environment]::GetEnvironmentVariable('PATH', 'User') + ";$destPath", 'User')

# ZIP削除
Remove-Item $outPath

これでC:\hashicorp\terraformterraform.exeが展開されます。

コンソールを再起動すればPATHが通るので、こんな感じでTerraformのコマンドを呼び出せる様になります。

試してみた

今回は以前の記事で作成した様なベストプラクティスに沿った高可用性ネットワークを作ってみます。

tfファイル

適当なディレクトリ(本記事ではC:\temp\tfsample\)に以下の2ファイルを作成します。

AWS認証情報の設定方法は様々ですが、今回はお試しということもありProvider設定に直接Access Key/Secret Keyを記述しています。
認証情報の記述は環境に応じて適切な方式を選んでください。

  • main.tf
provider "aws" {
  access_key = "<your access key>"
  secret_key = "<your secret key>"
  region     = "ap-northeast-1"
}

resource "aws_vpc" "vpc" {
  cidr_block           = "172.16.0.0/16"
  instance_tenancy     = "default"
  enable_dns_support   = "true"
  enable_dns_hostnames = "true"
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-vpc"
  }
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-igw"
  }
}

resource "aws_subnet" "public_subnet1" {
  vpc_id     = aws_vpc.vpc.id
  cidr_block = "172.16.1.0/24"
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-public-subnet1"
  }
}
resource "aws_subnet" "public_subnet2" {
  vpc_id     = aws_vpc.vpc.id
  cidr_block = "172.16.2.0/24"
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-public-subnet2"
  }
}
resource "aws_route_table" "public_rt1" {
  vpc_id = aws_vpc.vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-rt1"
  }
}
resource "aws_route_table_association" "public-rta1" {
  subnet_id      = aws_subnet.public_subnet1.id
  route_table_id = aws_route_table.public_rt1.id
}
resource "aws_route_table_association" "public-rta2" {
  subnet_id      = aws_subnet.public_subnet2.id
  route_table_id = aws_route_table.public_rt1.id
}

resource "aws_eip" "eip1" {
  vpc = true
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-eip1"
  }
}
resource "aws_nat_gateway" "ngw1" {
  allocation_id = aws_eip.eip1.id
  subnet_id     = aws_subnet.public_subnet1.id
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-ngw1"
  }
}
resource "aws_eip" "eip2" {
  vpc = true
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-eip2"
  }
}
resource "aws_nat_gateway" "ngw2" {
  allocation_id = aws_eip.eip2.id
  subnet_id     = aws_subnet.public_subnet2.id
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-ngw2"
  }
}

resource "aws_subnet" "private_subnet1" {
  vpc_id     = aws_vpc.vpc.id
  cidr_block = "172.16.11.0/24"
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-private-subnet1"
  }
}
resource "aws_subnet" "private_subnet2" {
  vpc_id     = aws_vpc.vpc.id
  cidr_block = "172.16.12.0/24"
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-private-subnet2"
  }
}
resource "aws_route_table" "private_rt1" {
  vpc_id = aws_vpc.vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_nat_gateway.ngw1.id
  }
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-rt1"
  }
}
resource "aws_route_table_association" "private-rta1" {
  subnet_id      = aws_subnet.private_subnet1.id
  route_table_id = aws_route_table.private_rt1.id
}
resource "aws_route_table" "private_rt2" {
  vpc_id = aws_vpc.vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_nat_gateway.ngw2.id
  }
  tags = {
    Name = "${var.system_name}-${terraform.workspace}-rt2"
  }
}
resource "aws_route_table_association" "private-rta2" {
  subnet_id      = aws_subnet.private_subnet2.id
  route_table_id = aws_route_table.private_rt2.id
}
  • variable.tf
variable "system_name" {
    type = "string"
    default = "mysystem"
    description = "Your System Name"
}

terraform init

tfファイルを記述したのでterraform initコマンドでバックエンドの初期化処理を行います。
今回はバックエンドの設定はデフォルトのままローカルファイルで行います。

cd C:\temp\tfsample
terraform init

.terraformフォルダ配下にTerraform AWS providerなどの実行ファイルがインストールされ初期処理が完了します。

terraform workspace

今回はtfファイル中でワークスペース名を使っているのでterraform workspaceコマンドで適当なワークスペース(dev)を定義しておきます。

terraform workspace new dev

terraform validate / plan

terraform validateterraform planコマンドで設定内容の確認を行います。

planの表示がCloudFormationよりあっさりして分かりやすいのが良いですね。

terraform apply

設定に問題がなければterraform applyコマンドで実環境に反映させます。
今回は特に追加パラメーターは指定せず実行します。

terraform apply

途中で処理を続行させるか確認されるのでyesを入力して続行します。
以降はAWSリソースが作成されていきます。

無事リソースの作成が完了しました。

terraform destroy

作ったリソースが不要になったらterraform destroyで環境を破壊します。
この場合も途中で処理を続行させるか確認されるのでyesを入力して続行します。

terraform destroy

最後に

ざっとこんな感じです。
Windows環境でも特別なことを意識せずにTerraformは使えました。

TerraformのAWS Providerで定義されるリソース構成は割とCloudFormationのリソース構成と近い印象を受け、HCLコードの記述量や見通しの良さを考えるとベストプラクティスとか深く考えずとりあえずTerraformで環境定義を書いてしまうのもアリかなぁという印象を受けました。
CloudFormationとTerraformの使い分けについてまだ自分の中の答えは出ていませんが、しばらくは両方使いながら考えていきたいと思います。

補足

補足として環境面でハマった点について触れておきます。

今回はエディタにVisual Studio Code + Terraform拡張 Ver.1.4.0を使いましたが、Terraform拡張 Ver.1.4.0ではHCL 2.0(Terraform 0.12以降)の文法には正式に対応しておらず、試験的な機能としてHCL 2.0対応がなされています。

今回試験的な機能を有効にしてHCLSP 2.0に対応させてみたところterraform plan / terraform applayといったコマンドがタイムアウトする事象に遭遇してしまいました。
おそらくGitHubの以下のIssueに該当すると思うのですが、いまいちはっきりしません...

私は試せていませんがもしかしたらWindows以外の環境でも発生するかもしれませんのでVisual Studio Code + Terraform拡張をお使いの方は気を付けてください。