開発

勉強会用資料 Terraformハンズオン

Terraform

社内向けのTerraformハンズオン勉強会用資料として作成

 

Terraform導入メリット

技術負債を軽減するためのもの

  • クラウドのデザインが変わる問題の解消
    スクショ手順書が死ぬ脆弱性を回避できる
  • develop, staging productionと環境でのリソース作成漏れ防止 … 障害防止
  • 再現、デプロイがスムーズ
  • TerraformのコードとAWSの実態が=になる。実装がドキュメントになる
  • プログラムと同じようにレビューができるようになる
  • GitLabと組み合わせて履歴、バージョン管理ができる
  • リソースの検索がVS Codeで簡単にできる
    AWSみたいにページ遷移しての確認が不要。設定のプロパティの一覧性もファイル内で完結。

 

ハンズオンの目的

なんだか難しそう…。
結構簡単かも!?🐱 ✨
作れてからなんで動くのか考える

 

 

完成するもの

 

 

事前準備

 

MacにAWS Cliインストール

 

$ sudo easy_install pip
$ sudo pip install awscli

 

aws cliのprofile 設定

 

事前に「hanson-terraform-deployer」なるIAMユーザを作っておきました。AdministratorAccess権限が付与されています。

こちらのユーザをMacに登録します

 

% aws configure --profile hanson-terraform-deployer

AWS Access Key ID [None]: {シークレットキーID}
AWS Secret Access Key [None]: {シークレットキー}
Default region name [None]: ap-northeast-1
Default output format [None]: json

これで設定できました。

確認する場合

% cat ~/.aws/credentials

[hanson-terraform-deployer]
aws_access_key_id = {シークレットキーID}
aws_secret_access_key = {シークレットキー}

 

MacにTerraformのインストール

Terraformのインストール

 

$ brew install tfenv

 

$ tfenv -v

tfenv 2.2.2

安定している1.0.11を使います。

$ tfenv list-remote

1.1.0-beta1
1.1.0-alpha20211029
1.1.0-alpha20211020
1.1.0-alpha20211006
1.1.0-alpha20210922
1.1.0-alpha20210908
1.1.0-alpha20210811
1.1.0-alpha20210728
1.1.0-alpha20210714
1.1.0-alpha20210630
1.1.0-alpha20210616
1.0.11
1.0.10
1.0.9
1.0.8

 

1.0.11インストール
$ tfenv install 1.0.11

1.0.11を使う
$ tfenv use 1.0.11
Switching default version to v1.0.11
Switching completed

 

これでMacにTerraformが入りました。

 

VSCodeの拡張機能をインストール

 

AWSコンソールにログイン

 

コンソール画面にログインできることを確認してください。

Terraformで作ったリソースを確かめることができます!

 

 

ハンズオン開始!

こんなディレクトリ構造になります。

ハンズオン用リポジトリをgit cloneでダウンロードします。

$ git clone https://github.com/yuukanehiro/bs-terraform-hanson2022-02.git

 

tfstateを管理するS3バケットの作成

 

事前にtfstateを格納するS3バケット「tfstate-handson-484711902108」は手動で作っておきましたので、
ショートカットします。

 

 

ディレクトリを作成して、作成したディレクトリに移動します

$ mkdir -p /providers/aws/environments/test/{あなたの名前}
$ cd /providers/aws/environments/test/{あなたの名前}/

そこがあなたの作業ディレクトリになります。

 

変数ファイル作成

terraform.tfvars

aws_region             = "ap-northeast-1"
aws_profile            = "hanson-terraform-deployer"
ENV_VALUE_ENVIRONMENT  = "test"
TAG_PROJECT            = "terraform-handson"
TAG_ROLE               = "handon"
TAG_AUTHOR             = "yuu3" # 🔥 自分の名前にしてください 例) tanaka

 

 

 

main.tfファイル作成

 

terraform {
  required_version = "= 1.1.1"
  backend "s3" {
    bucket = "tfstate-handson-12345678" # S3のバケット。事前に作ってあります。
    region = "ap-northeast-1"
    key     = "{🔥ここに名前を英数入力お願いします}/terraform.tfstate" #  例) "yuu3/terraform.tfstate"
    profile = "hanson-terraform-deployer" # 🐱 AWS Cliで設定したIAMユーザのプロフィール名
  }
}

variable aws_region {}
variable aws_profile {}
variable ENV_VALUE_ENVIRONMENT {}
variable TAG_PROJECT {}
variable TAG_ROLE {}
variable TAG_AUTHOR {}

provider "aws" {
  region  = var.aws_region
  profile = var.aws_profile
  default_tags {
    tags = {
      env     = var.ENV_VALUE_ENVIRONMENT
      project = var.TAG_PROJECT
      role    = var.TAG_ROLE
      author  = var.TAG_AUTHOR
    }
  }
}

 

 

初期化します。

% terraform init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v3.74.0...
- Installed hashicorp/aws v3.74.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.

 

VPCの作成

vpc.tf作成

 

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

 

terraform planによる実行計画の確認

% 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:

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_classiclink                   = (known after apply)
      + enable_classiclink_dns_support       = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags_all                             = {
          + "author"  = "kanehiro"
          + "env"     = "test"
          + "project" = "terraform-handson"
          + "role"    = "handon"
        }
    }

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.

 

terraform applyによる実行

% 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:

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_classiclink                   = (known after apply)
      + enable_classiclink_dns_support       = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags_all                             = {
          + "author"  = "kanehiro"
          + "env"     = "test"
          + "project" = "terraform-handson"
          + "role"    = "handon"
        }
    }

Plan: 1 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 🔥 yesと入力してEnterしてください
  
  aws_vpc.main: Creating...
aws_vpc.main: Creation complete after 2s [id=vpc-08624fd5aa20406d0]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

あなたのVPCが作られたことを確認してください。

authorタグから確認できます。

 

サブネットpublic_1aの作成

subnet.tf
# Subnet
# https://www.terraform.io/docs/providers/aws/r/subnet.html
resource "aws_subnet" "public_1a" {
  # 先程作成したVPCを参照し、そのVPC内にSubnetを立てる
  vpc_id = aws_vpc.main.id # 🐱 先ほど作ったVPCのIDを動的に指定しています。

  # Subnetを作成するAZ
  availability_zone = "ap-northeast-1a"

  cidr_block        = "10.0.1.0/24"

  tags = {
    Name = "handson-public-1a"
  }
  depends_on = [aws_vpc.main] # 🐱 依存関係を指定しています。vpcがある前提であることを明示しています。同時にリソースを作る際に重要
}

実行計画立てて確認しましょう。

 % terraform plan 

aws_vpc.main: Refreshing state... [id=vpc-08624fd5aa20406d0]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # aws_vpc.main has changed
  ~ resource "aws_vpc" "main" {
        id                               = "vpc-08624fd5aa20406d0"
      + tags                             = {}
        # (16 unchanged attributes hidden)
    }


Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to
undo or respond to these changes.

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

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:

  # aws_subnet.public_1a will be created
  + resource "aws_subnet" "public_1a" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-1a"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "handson-public-1a"
        }
      + tags_all                                       = {
          + "Name"    = "handson-public-1a"
          + "author"  = "kanehiro"
          + "env"     = "test"
          + "project" = "terraform-handson"
          + "role"    = "handon"
        }
      + vpc_id                                         = "vpc-08624fd5aa20406d0"
    }

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.

 

 

問題なければ実行しましょう。

% terraform apply
aws_vpc.main: Refreshing state... [id=vpc-08624fd5aa20406d0]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # aws_vpc.main has changed
  ~ resource "aws_vpc" "main" {
        id                               = "vpc-08624fd5aa20406d0"
      + tags                             = {}
        # (16 unchanged attributes hidden)
    }


Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to
undo or respond to these changes.

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

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:

  # aws_subnet.public_1a will be created
  + resource "aws_subnet" "public_1a" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-1a"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "handson-public-1a"
        }
      + tags_all                                       = {
          + "Name"    = "handson-public-1a"
          + "author"  = "kanehiro"
          + "env"     = "test"
          + "project" = "terraform-handson"
          + "role"    = "handon"
        }
      + vpc_id                                         = "vpc-08624fd5aa20406d0"
    }

Plan: 1 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 🔥 yesと入力

aws_subnet.public_1a: Creating...
aws_subnet.public_1a: Creation complete after 1s [id=subnet-04770c2b153ca7738]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.




 

subnet.tfにもう1つサブネットpublic_1cを作ろう

 

subnet.tf
# Subnet
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet
resource "aws_subnet" "public_1a" {
  # 先程作成したVPCを参照し、そのVPC内にSubnetを立てる
  vpc_id = aws_vpc.main.id # 🐱  先ほど作ったVPCのIDを動的に指定しています

  # Subnetを作成するAZ
  availability_zone = "ap-northeast-1a"

  cidr_block        = "10.0.1.0/24"

  tags = {
    Name = "handson-public-1a"
  }
  depends_on = [aws_vpc.main] # 🐱  依存関係を指定しています。vpcがある前提であることを明示しています。同時にリソースを作る際に重要
}


# 🔥🔥🔥 下記を追加

resource "aws_subnet" "public_1c" {
  vpc_id = aws_vpc.main.id

  availability_zone = "ap-northeast-1c"

  cidr_block        = "10.0.2.0/24"

  tags = {
    Name = "handson-public-1c"
  }
}

 

$ terraform plan
$ terraform apply

 

Internet Gatewayを作ろう

internet_gateway.tf

 

# Internet Gateway
# https://www.terraform.io/docs/providers/aws/r/internet_gateway.html
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "handson-"
  }
}

 

$ terraform plan
$ terraform apply

 

Route Tableを作ろう

 

route_table.tf

# Route Table
# https://www.terraform.io/docs/providers/aws/r/route_table.html
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "handson-public"
  }
}

# Route
# https://www.terraform.io/docs/providers/aws/r/route.html
resource "aws_route" "public" {
  destination_cidr_block = "0.0.0.0/0"
  route_table_id         = aws_route_table.public.id
  gateway_id             = aws_internet_gateway.main.id
}

# Association
# https://www.terraform.io/docs/providers/aws/r/route_table_association.html
resource "aws_route_table_association" "public_1a" {
  subnet_id      = aws_subnet.public_1a.id
  route_table_id = aws_route_table.public.id
  depends_on     = [aws_route_table.public]
}
resource "aws_route_table_association" "public_1c" {
  subnet_id      = aws_subnet.public_1c.id
  route_table_id = aws_route_table.public.id
  depends_on     = [aws_route_table.public]
}

 

$ terraform plan
$ terraform apply

 

セキュリティグループを作ろう

sg.tf

# SecurityGroup
# https://www.terraform.io/docs/providers/aws/r/security_group.html
resource "aws_security_group" "http" {
  name        = "handson-alb"
  description = "handson alb"
  vpc_id      = aws_vpc.main.id

  # セキュリティグループ内のリソースからインターネットへのアクセスを許可する
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "handson-http"
  }
}

# SecurityGroup Rule
# https://www.terraform.io/docs/providers/aws/r/security_group.html
resource "aws_security_group_rule" "http" {
  security_group_id = aws_security_group.http.id

  # セキュリティグループ内のリソースへインターネットからのアクセスを許可する
  type = "ingress"

  from_port = 80
  to_port   = 80
  protocol  = "tcp"

  cidr_blocks = ["0.0.0.0/0"]
}

 

$ terraform plan
$ terraform apply

 

EC2を作ろう

variable.tf

variable "ami_id_demo" {
    default = "ami-0acaef956254d73c1" # 事前にWebサーバ(Nginx)のAMI作っておきました
    type = string
}

terraform.tfvars以外にもvariableディレクティブで定義できます。

ec2.tf

resource "aws_instance" "web1" {
    ami                    = var.ami_id_demo
    instance_type          = "t2.nano"
    monitoring             = true
    vpc_security_group_ids = [aws_security_group.http.id]
    subnet_id              = aws_subnet.public_1a.id
}

resource "aws_instance" "web2" {
    ami                    = var.ami_id_demo
    instance_type          = "t2.nano"
    monitoring             = true
    vpc_security_group_ids = [aws_security_group.http.id]
    subnet_id              = aws_subnet.public_1c.id
}

 

$ terraform plan
$ terraform apply

 

ALBを作ろう

alb.tf

# ALB
# https://www.terraform.io/docs/providers/aws/d/lb.html
resource "aws_lb" "main" {
  load_balancer_type = "application"
  name               = "alb-${var.TAG_AUTHOR}"

  security_groups = [aws_security_group.http.id]
  subnets = [
    aws_subnet.public_1a.id,
    aws_subnet.public_1c.id
  ]
}

# TargetGroup
resource "aws_lb_target_group" "main" {
  name     = "alb-${var.TAG_AUTHOR}-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id
}

resource "aws_lb_target_group_attachment" "web1" {
  target_group_arn = aws_lb_target_group.main.arn
  target_id        = aws_instance.web1.id
  port             = 80
  depends_on = [aws_instance.web1]
}
resource "aws_lb_target_group_attachment" "web2" {
  target_group_arn = aws_lb_target_group.main.arn
  target_id        = aws_instance.web2.id
  port             = 80
  depends_on = [aws_instance.web2]
}


# Listener
# https://www.terraform.io/docs/providers/aws/r/lb_listener.html
resource "aws_lb_listener" "main" {
  # HTTPでのアクセスを受け付ける
  port              = "80"
  protocol          = "HTTP"

  # ALBのarnを指定します。
  #XXX: arnはAmazon Resource Names の略で、その名の通りリソースを特定するための一意な名前(id)です。
  load_balancer_arn = aws_lb.main.arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.main.arn
  }
  depends_on = [
    aws_lb.main,
    aws_lb_target_group.main
  ]
}

 

$ terraform plan
$ terraform apply

確認


AWSコンソールからロードバランサーのページに行きます。

説明からDNS名欄でオレンジのリンクをクリックするとコピーされます。

ブラウザのURL欄に貼り付けて、Test Pageが表示されたら成功です!

 

お疲れ様でした!

 

最後はお片づけ

 

えーい、バルス!

$ terraform destroy

// develop, staging, production環境では禁止です。。

 

参考

Amazonおすすめ

iPad 9世代 2021年最新作

iPad 9世代出たから買い替え。安いぞ!🐱 初めてならiPad。Kindleを外で見るならiPad mini。ほとんどの人には通常のiPadをおすすめします><

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)