terraformを使ってIaCを目指す(立志編)

はじめに

はじめまして、インフラを担当しているKAMAKOです。
今さらですがterraformを使ってIaCを実現したいため動作検証を行っております。
今回は立志編ということで、terraform初心者がコード化しようと思った経緯と導入から簡単な自動構築実践までの検証内容を紹介します。
また、自分自身の備忘のためハマったポイントも記載していきます。

経緯

terraform導入により業務内容に改善できそうな箇所がいくつかあったため、まずはメリット/デメリットを洗い出しました。そこから比較検討した結果、受ける恩恵の方が大きいと判断したため導入に向けて検証することにしました。
私自身が手動構築と資料の更新が手間でやりたくなかったという理由ではありません。

【メリット】
 ■コスト削減
 ・構築作業を自動化することで作業時間の削減。
 ・管理時間(資料作成や修正など)を削減。
 ・リリーススピードの向上。

 ■リスク排除
 ・手動構築によるヒューマンエラー、環境差分の排除。
 ・コード管理による変更履歴の自動管理(gitlabなどを導入)。
 ・リリース時の切り戻しが容易。

【デメリット】
 ■導入の敷居が高い
 ・既存・新規環境で必要となるリソースのterraformコード化。
 ・既存運用設計・手順書の修正。
 ・terraformに慣れるまではマネジメントコンソールの誘惑に気持ちが揺らぐ。

環境概要と検証内容

今回はAWS環境にterraform実行用のEC2インスタンスを用意して動かしてみます。
なお、terraformを導入するEC2(Amazon Linux2)は手動構築にて作成済みとします。




【ポイント】
 ・専用のIAM Roleを用意し、必要権限を付与して構築を行う。

【検証内容】
 ・terraformのインストールと実行コマンドの確認。
 ・terraform構成ファイルの理解とコードの作成。
 ・IAM Roleを指定してterraformを実行する。

【ゴール】
 ・terraformで自動構築したEC2インスタンスにSSH接続する。

terraformインストール

今回はTerraform v1.0.1をインストールしました。
※aws cliのバージョンアップ手順については割愛します。

1.パッケージインストール(必要であれば)

$ rpm -qa | grep yum-utils
$ sudo yum install -y yum-utils
  yum-utils-1.1.31-46.amzn2.0.1.noarch

2.リポジトリダウンロード

$ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
$ ls -l /etc/yum.repos.d/hashicorp.repo
  /etc/yum.repos.d/hashicorp.repo

3.terraformインストール

$ sudo yum -y install terraform
$ terraform -v
  Terraform v1.0.1   #証時に使用したバージョンです

コード作成

今回はterraformとは何ぞやレベルの検証のため、moduleなどは作成せず最小限のコード作成のみとします。

■ valiables.tf
 認証情報、リージョン情報、AMIの変数を定義。

variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region" {}
variable "ec2-ami" {}

■ terraform.tfvars
 認証情報、リージョン情報、AMIを記載。
 ※今回は検証のためファイルに直接記載していますが、exportコマンドで環境変数とした方が安全です。
  キー情報の管理には十分注意してください

# access_key
aws_access_key = "*** アクセスキー情報 ***"

# secret_key
aws_secret_key = "*** シークレットキー情報 ***"

# region
region = "ap-northeast-1"   # Tokyo

# ec2 ami
ec2-ami = "ami-0b276ad63ba2d6009"   # amzn2-ami-hvm-2.0.20210701.0-x86_64-gp2

■ main.tf
 terraform基本情報、認証情報、作成するAWSサービスを記載。

### terraform version
terraform {
    required_version = "= 1.0.1"
}

### provider ###
provider "aws" {
    access_key = "${var.aws_access_key}"
    secret_key = "${var.aws_secret_key}"
    region = "${var.region}"
    assume_role {
        role_arn = "arn:aws:iam::*** AWSアカウントID ***:role/*** ロール名 ***"
        session_name = "terraform"
    }
}

### VPC ###
resource "aws_vpc" "terraform-vpc" {
    cidr_block = "***.***.***.***/**"
    instance_tenancy = "default"
    enable_dns_support = "true"
    enable_dns_hostnames = "true"
    tags = {
        Name = "terraform-vpc"
    }
}

### Internet Gateway ###
resource "aws_internet_gateway" "terraform-igw" {
    vpc_id = "${aws_vpc.terraform-vpc.id}" # VPCのID属性を参照
    tags = {
        Name = "terraform-igw"
    }
}

### subnet ###
resource "aws_subnet" "terraform-subnet-a" {
    vpc_id = "${aws_vpc.terraform-vpc.id}" # VPCのID属性を参照
    cidr_block = "***.***.***.***/**"
    availability_zone = "ap-northeast-1a"
    tags = {
        Name = "terraform-subnet-a"
    }
}

### route table ###
resource "aws_route_table" "terraform-route-table" {
    vpc_id = "${aws_vpc.terraform-vpc.id}" # VPCのID属性を参照

    route {
        cidr_block = "***.***.***.***/**"
        gateway_id = "${aws_internet_gateway.terraform-igw.id}" # Internet GatewayのID属性を参照
    }
    tags = {
        Name = "terraform-route-table"
    }
}

### route tables association ###

resource "aws_route_table_association" "terraform-route-table-a" {
    subnet_id = "${aws_subnet.terraform-subnet-a.id}"
    route_table_id = "${aws_route_table.terraform-route-table.id}"
}

### security_group ###
resource "aws_security_group" "terraform-sg" {
    name = "terraform-sg"
    description = "Allow SSH inbound traffic"
    vpc_id = "${aws_vpc.terraform-vpc.id}" # VPCのID属性を参照
    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = "***.***.***.***/**"
        description = "allow ssh"
    }
    egress {
        from_port = 0
        to_port = 0
        protocol = "-1"
        cidr_blocks = ["0.0.0.0/0"]
    }
    tags = {
        Name = "terraform-sg"
    }
}


### ec2 ###
resource "aws_instance" "terraform-ec2" {
    ami = "${var.ec2-ami}"
    instance_type = "t2.micro"
    key_name = "terraform-key"
    vpc_security_group_ids = [
        "${aws_security_group.terraform-sg.id}"
    ]
    subnet_id = "${aws_subnet.terraform-subnet-a.id}"
    associate_public_ip_address = "true"
    root_block_device {
        volume_type = "gp2"
        volume_size = "20"
    }
    ebs_block_device {
        device_name = "/dev/sdb"
        volume_type = "gp3"
        volume_size = "100"
    }
    tags = {
        Name = "terraform-ec2"
    }
}

### EIP ###
resource "aws_eip" "terraform-ec2-eip" {
    instance = "${aws_instance.terraform-ec2.id}"
    vpc = true
    tags = {
        Name = "terraform-ec2-eip"
    }
}

コード作成でハマったポイント

※弊社の運用事情となりますが、特定のIAM Roleにのみ権限を付与しており
 Assume Roleしてからterraformを実行しないと権限不足で構築ができません。
 そのためassume roleの情報を必ず記載する必要があります。
 (検証時はここで数時間ハマりました)

    assume_role {
        role_arn = "arn:aws:iam::*** AWSアカウントID ***:role/*** ロール名 ***"
        session_name = "terraform"
    }

terraform実行と接続確認

1.作成したファイルの配置
 /usr/local/terraform ディレクトリを作成し配置しました。

2.terraform実行

$ terraform validate  # 構文チェック

$ terraform plan      # 作成したコードのドライ・ラン
  Plan: 2 to add, 0 to change, 0 to destroy.

$ terraform apply     # コード内容の適用
  Apply complete! Resources: 6 added 0 changed, 0 destroyed.

3.環境構築の確認
 マネジメントコンソールからコード化したAWSサービスが作成されていることを確認。
 正常に作成されていました。

4.接続確認
 terraformで構築したEC2インスタンスに踏み台環境を経由して接続できることを確認しました。

まとめ

簡易環境の構築のみでしたが、コードを1から作成するのはかなり大変でした。
既存の手動構築で作成した環境をあえてコード化するよりは、定期的に発生する作業や複数環境で共通となる設定などをコード化していくのがベストな気がしています。また、構築や管理作業の負担の軽減が目的であり、コード化することが目的ではないということを常に意識する必要があると感じました。
冒頭でも記載しましたが、導入の敷居は高いですが自動構築とコード管理の恩恵は非常に大きいため少しずつではありますが、コード化を推し進めていきたいと思います。
楽するために苦労を買ってしまうのはエンジニアの性な気がします…

今後の野望

  1. gitlabやgithubなどでコード管理。
  2. 社内にterraformを普及。
  3. terraform実行環境のベストプラクティス設計。
  4. 共通moduleの作成。
  5. 特定条件をトリガーとしてterraform自動実行など(実現できるのか今後調査)、どんどん自動化してみたい。
  6. 最終的には手動構築と資料管理の手間を極限まで減らしたい

おまけ

terraformで作成したリソースを全て削除する。

$ terraform destroy