0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【備忘録】Terraformの仕組みを整理する

Last updated at Posted at 2021-08-09

#本記事の目的
社内でのクラウド検証に合わせて、Terraformの基礎知識を学び、アウトプットとして備忘録を記す。
本記事ではTerraformの基本操作、基本構文、及び基本的なAWSリソースの作成方法について説明する。

#主要コマンド
####1.terraform init
terraform実行に必要なバイナリファイルを準備する。
このコマンドを最初に実施する事で、他のterraformコマンドが実施できるようになる。

####2.terraform validate
設定ファイルが有効かどうかをチェックする。

####3.terraform plan
設定ファイルに従い、リソースの実行計画を出力する。

####4.terraform apply
設定ファイルに従い、リソースを作成・変更する。

####5.terraform destroy
設定ファイルに従って作成したリソースを削除する。

#設定ファイル
####1.tfファイル
terraformで作成・変更するリソースを定義したファイル。ユーザー自身が作成する。

####2.tfvarsファイル
tfファイルに読み込ませたい変数を定義したファイル。ユーザー自身が作成する。

####3.tfstateファイル
現時点でのterraformで作成・変更されたリソースの状態を記録したファイル。terraform applyコマンドを実行すると、自動的に作成・変更される。

#tfファイルにおける基本ブロック
####1.resource
作成・変更するリソースを定義する。

resource.tf
resource "aws_instance" "web" {
  ami           = "ami-a1b2c3d4"
  instance_type = "t2.micro"
}

####2.data
データソースを定義し、外部データの参照する。

data.tf
data "aws_ami" "example" {
  most_recent = true

  owners = ["self"]
  tags = {
    Name   = "app-server"
    Tested = "true"
  }
}

####3.provider
リソースを作成するのに必要なクラウドプロバイダー、Saasプロバイダー、その他APIを定義する。

provider.tf
provider "aws" {
  region = "ap-northeast-1"
}

####4.variable
グローバル変数を定義する。

variable.tf
variable "image_id" {
  type = string
}

variable "availability_zone_names" {
  type    = list(string)
  default = ["ap-northeast-1"]
}

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

####5.locals
ローカル変数を定義する。

locals.tf
locals {
  service_name = "forum"
  owner        = "Community Team"
}

####6.output
出力値を定義し、terraform applyコマンド実行時に特定のデータを出力する。

output.tf
output "instance_ip_addr" {
  value = aws_instance.server.private_ip
}

####7.module
モジュールを定義し、特定のディレクトリ配下の設定ファイルを利用する。

module.tf
module "servers" {
  source = "./app-cluster"

  servers = 5
}

#代表的なAWSリソースの作成方法

####1.IAM

主要resource名 機能
aws_iam_user IAMユーザの作成 
aws_iam_user_login_profile ログインプロファイルの作成
aws_iam_access_key アクセスキーの作成
aws_iam_group IAMグループの作成
aws_iam_policy 管理ポリシーの作成
aws_iam_group_policy_attachment IAMグループに対する管理ポリシーのアタッチ
aws_iam_user_group_membership IAMグループへのIAMユーザの追加
aws_iam_role IAMロールの作成
aws_iam_role_policy_attachment IAMロールに対する管理ポリシーのアタッチ
  • 例①:IAMユーザ/ログインプロファイル/アクセスキーの作成(ログインパスワードとシークレットアクセスキーを出力)
example_iam_user.tf
resource "aws_iam_user" "test-user" {
  name          = "taro"
  path          = "/"
}

resource "aws_iam_access_key" "test-user" {
  user    = aws_iam_user.test-user.name
  pgp_key = "keybase:some_person_that_exists"
}

resource "aws_iam_user_login_profile" "taro" {
  user    = aws_iam_user.test-user.name
  pgp_key = "keybase:some_person_that_exists"
}

output "secret" {
  value       = aws_iam_access_key.test-user.encrypted_secret
  description = "IAMユーザの暗号化されたパスワード"
}

output "password" {
  value       = aws_iam_user_login_profile.test-user.encrypted_password
  description = "IAMユーザの暗号化されたシークレットキー"
}
  • 例②:IAMグループを作成、IAMユーザを作成しIAMグループに追加、管理ポリシーを作成しIAMグループにアタッチ
example_iam_group.tf
resource "aws_iam_user_group_membership" "test-membership" {
  user = aws_iam_user.test-user.name

  groups = [
    aws_iam_group.test-group.name,
  ]
}

resource "aws_iam_user" "test-user" {
  name = "jiro"
}

resource "aws_iam_group" "test-group" {
  name = "engineer"
}

resource "aws_iam_policy" "test-policy" {
  name        = "EC2 describe policy"
  description = "A test policy"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "ec2:Describe*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}

resource "aws_iam_group_policy_attachment" "test-attach" {
  group      = aws_iam_group.test-group.name
  policy_arn = aws_iam_policy.test-policy.arn
}
  • 例③:IAMロールを作成、管理ポリシーを作成しIAMロールにアタッチ
example_iam_role.tf
resource "aws_iam_role" "test-role" {
  name = "test-role"
}

resource "aws_iam_policy" "test-policy" {
  name        = "EC2 describe policy"
  description = "A test policy"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "ec2:Describe*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "test-attach" {
  role       = aws_iam_role.test-role.name
  policy_arn = aws_iam_policy.test-policy.arn
}

####2.EC2/VPC

主要resource名 機能
aws_instance EC2インスタンスの作成
aws_key_pair キーペアの作成
aws_vpc VPCの作成 
aws_subnet サブネットの作成
aws_internet_gateway インターネットゲートウェイの作成
aws_route_table ルーティングテーブルの作成
aws_route ルーティングテーブルに対するルートの追加
aws_route_table_association ルーティングテーブルとサブネットの関連付け
aws_security_group セキュリティグループの作成
aws_security_group_rule セキュリティグループに対するルールの追加
  • 例①:VPC作成、パブリックサブネット作成、EC2インスタンス起動(パブリックIPを出力)
example_instance.tf
resource "aws_instance" "test-instance" {
  ami                    = "ami-0fccdb46e227b9538" # ap-northeast-1
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.test-public-subnet.id
  vpc_security_group_ids = [aws_security_group.test-sg.id]
  key_name               = aws_key_pair.test-key.id
}

resource "aws_key_pair" "test-key" {
  key_name   = "test-key"
  public_key = file("./tf-test.pub") # `ssh-keygen`コマンドで作成した公開鍵を指定
}

resource "aws_vpc" "test-vpc" {
  cidr_block = "10.0.0.0/16"
  enable_dns_support   = true # DNS解決有効化
  enable_dns_hostnames = true # DNSホスト名有効化

  tags = {
    Name = "my-vpc"
  }
}

resource "aws_subnet" "test-public-subnet" {
  vpc_id            = aws_vpc.test-vpc.id
  cidr_block        = "10.0.10.0/24"
  map_public_ip_on_launch = true #インスタンス起動時におけるパブリックIPアドレスの自動割り当ての有効化

  tags = {
    Name = "my-public-subnet"
  }
}

resource "aws_internet_gateway" "test-ingw" {
  vpc_id = aws_vpc.test-vpc.id

  tags = {
    Name = "my-ingw"
  }
}

resource "aws_route_table" "test-public-rt" {
  vpc_id = aws_vpc.test-vpc.id
  tags = {
    Name = "my-public-route-table"
  }
}


resource "aws_route" "test-public-route" {
  route_table_id         = aws_route_table.test-public-rt.id
  gateway_id             = aws_internet_gateway.test-ingw.id
  destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "test-public-subrt" {
  subnet_id      = aws_subnet.test-public-subnet.id
  route_table_id = aws_route_table.test-public-route.id
}

resource "aws_security_group" "test-sg" {
  name        = "test-sg"
  vpc_id      = aws_vpc.test-vpc.id

  tags = {
    Name = "test-sg"
  }
}

resource "aws_security_group_rule" "in_ssh" {
  security_group_id = aws_security_group.test-sg.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
}

resource "aws_security_group_rule" "in_icmp" {
  security_group_id = aws_security_group.test-sg.id
  type              = "ingress" #インバウンドルール
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = -1
  to_port           = -1
  protocol          = "icmp"
}

resource "aws_security_group_rule" "out_all" {
  security_group_id = aws_security_group.test-sg.id
  type              = "egress" #アウトバウンドルール
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
}

output "public_ip" {
  value       = aws_instance.test-instance.public_ip
  description = "EC2インスタンスのパプリックIP"
}

####3.S3

主要resource名 機能
aws_s3_bucket S3バケットの作成 
aws_s3_bucket_object オブジェクトのアップロード
  • 例①:S3バケット作成、バージョニング有効化、暗号化
example_s3.tf
resource "aws_s3_bucket" "test-bucket" {
  bucket = "s3-test-bucket"
  acl    = "private"

  versioning {
    enabled = true
  } # バージョニング

  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm     = "AES256"
      }
    } # 暗号化
  }

  tags = {
    Name        = "My bucket"
  }
}
  • 例②:S3バケット作成、静的Webサイトホスティング有効化、オブジェクトアップロード
example_s3_website.tf
resource "aws_s3_bucket" "test-website-bucket" {
  bucket = "s3-website-test.com"
  acl    = "public-read" #静的Webサイトホスティング有効化

  website {
    index_document = "index.html"
    error_document = "error.html"
  }

  tags = {
    Name        = "My website bucket"
  }
}

resource "aws_s3_bucket_object" "test-index" {
  bucket = aws_s3_bucket.test-bucket.id
  key    = "index.html"
  source = "/work/index.html" # 作成した`index.html`を指定
}

resource "aws_s3_bucket_object" "test-error" {
  bucket = aws_s3_bucket.test-bucket.id
  key    = "error.html"
  source = "/work/error.html" # 作成した`error.html`を指定
}

#感想
Terraformに限らないが、IaCツールは同じインフラ構成を再現する際には非常に有用であるものの、既存のインフラ構成を変化させる場合に柔軟に対応しにくいという欠点も存在する。

そのためインフラの自動化を追求する場合は、変化が少ないインフラ構成は再現性の高いIaCツールで実現し、変化が多いインフラ構成は柔軟性の高いCLIツールで都度実現するというように、インフラ構成に応じて使い分ける方が重要であると考える。

今後もIaCとCLI両面で、インフラ自動化について探求してみたい。
(ちなみに「代表的なAWSリソースの作成方法」の章については、もう少し別のAWSサービスのtfファイルもまとめてみようか検討中・・・)

#参考文献

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?