[ハンズオン] Terraform Registry を使ったVPC/Sg/EC2作成

2月 13, 2022【初学者向け】実践演習,AWS,EC2,terraform,VPC

概要

  • Terraformハンズオンの第一弾です。今回は、AWS環境にVPC/EC2 のリソースを作成します。
  • なお、今回は、Terraform Registry のModule を使用して構築を行います。このハンズオンのポイントは、Terraform Registry のサンプルをできるだけ組み合わせて作成できる内容となっています。

 

対象読者

  • Terraform 初心者向けの記事です。AWSユーザーであれば、Terraform 利用のメリットは理解しているが、Terraform を一から記述する学習コストを惜しんでコンソールからリソース作成する方も多いかと思います。私にとっても、あるあるです..。
  • その様な初心者向けのためにも、今回のハンズオンは、Terraform Registry を使用します! Terraform Registry は、あなたのTerraform 学習コストを減らし、あなたがTerraform を理解する近道になると考えます。是非、この記事を参考に、Teraform の活用を進めて下さい。。

 

Terraform Registryとは

  • Terraform Registry はTerraformの公式モジュールレジストリで、HashiCorpの公式やサードベンダ、個人の開発者が作成したProviderModuleが公開され、利用することができます。ModuleはHashiCorpの公式であるofficial以外に、HashiCorpによって認証・テストされたverified、その他のコミュニティよって公開されているcommunity に分類されています。AWS、Azure、GCP、OCIなどメジャーなCloud ベンダだけでなく、多くのソフトウェアベンダのProvider が利用できます。

 

Terraform のModule とは

  • Terraformは、メインとなるルートモジュールから他のモジュールを呼び出すことができます。モジュール化によって、子モジュールのリソースを簡潔に記述できます。モジュールは複数回呼び出すこともできるため、リソース構成をパッケージ化して再利用できます。モジュールの呼び出しに必要な値を与えることで、環境に合わせた構築ができます。
  • 今回は、Terraform Registryで公開されているModule を利用するため、ローカルにはModule のコードを記述しません。main.tf だけでModule の恩恵を受けることができます。

 

 

ハンズオン1:VPC作成

  • ハンズオン1は、AWS環境に、VPCを作成します。VPC 以外のリソースは、Internet Gateway、NAT Gateway、Public Subnet(Internet Gatewayにルーティングが関連付けられたサブネット)、Private Subnet(NAT Gatewayにルーティングが関連付けられたサブネット)、ルーティングテーブルを作成します。

 

構成図

  • 以下、ハンズオン1の構成図です。リージョンは、バージニア北部(us-east-1)を使用します。好みに応じて、変更してください。
  • サブネットは、3AZ構成とします。NAT Gateway は、各AZ に配置します。 ※NAT Gateway に利用料が掛かるため、ハンズオンの終了後にterraform はdestroy しましょう。

 

事前準備

  • Terraform がインストールされ、AWS環境のIAM およびIAMに対応したクレデンシャルが取得されている前提です。
  • クレデンシャルは、シェルの環境変数(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)でアクセスキーを渡す方法やTerraform の環境変数にクレデンシャルファイル、プロファイルを指定する方法など方法は複数あります。なお、今回は、クレデンシャルの設定について詳細は記載しません。クレデンシャルの設定方法は、こちらの記事を参照ください。(ハンズオンのサンプルでは、シェルの環境変数を使用したアクセスキー設定の例を載せております)
  • 始めに、main.tf, variables.tf, outputs.tf, provider.tf, backend.tf を作成します。main.tf がコードを記述するエントリポイントになります。今回は、variables.tf, outputs.tf は使用しませんが、この2つはmain.tf に対する入力値と出力値になります。
  • 次に、provider.tf の記述例です。provider.tf には、最低限1つのprovider を定義し、region を指定します。
provider "aws" {
  region                  = "us-east-1"
}

 

  • backend.tf の記述例です。tfstateファイルはデフォルトでローカルに保存されますが、チームで共有する場合は、tfstateファイルをS3 に保存します。事前に、S3 バケットを作成し、パブリックアクセスをブロックに設定します。また、tfstateファイルを管理するバケットのバージョニング設定を有効にすることも良いと思います。
  • terraform apply を実行後に、自動的にS3 バケットにtfstateファイルが作成されます。
# tfstate をローカル管理する場合
#terraform {
#  backend "local" {
#    path = "./terraform.tfstate"
#  }
#}

# tfstate をS3管理する場合
terraform {
  backend "s3" {
    bucket  = "bucket-name"
    region  = "us-east-1"
    key     = "dir/terraform.tfstate"
    encrypt = false
# S3のAWSアカウントがデプロイ先と異なる場合に指定
#    access_key = ""
#    secret_key = ""
  }
}

 

tfコード作成

  • AWS VPC Terraform module にアクセスします。

 

  • Provision Instructions の記述をコピーし、main.tf に貼りつけます。

 

  • こちらのUsage のサンプルを参考にして、VPC、サブネットおよびその他のリソースを作成するためのVPC Moduleのコードを記述します。
  • 必要に応じて、VPCおよび関連リソースのPrefix となる"name" を指定します。環境に合わせて、VPC のCIDR、サブネットのCIDR を指定します。
    • module “sample_vpc" {
      source = “terraform-aws-modules/vpc/aws"
      name = “niikawa-test"
      cidr = “172.29.0.0/16"
      enable_dns_hostnames = true
      enable_dns_support = true
      azs = [“us-east-1a", “us-east-1b", “us-east-1c"]
      public_subnets = [“172.29.0.0/24", “172.29.1.0/24", “172.29.2.0/24"]
      private_subnets = [“172.29.10.0/24", “172.29.11.0/24", “172.29.12.0/24"]}

 

  • 次は、こちらのUsage に記載されているNAT Gatewayに関するサンプルとなります。
  • 今回は、NAT Gateway を各AZ に配置するため、VPC Moduleに下記のパラメータを追加します。
    • enable_nat_gateway = true
    • single_nat_gateway = false
    • one_nat_gateway_per_az = true
  • デフォルトは、NAT Gateway 作成時に、新しいEIP (Elastic IP)が割り当てられます。今回は、EIPを保持し、NAT Gateway 再作成時に同じEIP を割り当てる設定を行いたいと思います。これを実現するため、VPC Module 定義の外側に下記のリソースを作成します。
    • resource “aws_eip" “nat" {
      count = 3vpc = true
      }
  • VPC Module に下記を追加し、作成したEIP をパラメータとして受けます。
    • reuse_nat_ips = true
    • external_nat_ip_ids = aws_eip.nat.*.id

 

  • サンプルコードを元に作成したコードは、以下となります。
module "sample_vpc" {
  source = "terraform-aws-modules/vpc/aws"
  name = "niikawa-test"
  cidr = "172.29.0.0/16"

  enable_dns_hostnames = true
  enable_dns_support   = true

  azs            = ["us-east-1a", "us-east-1b", "us-east-1c"]
  public_subnets = ["172.29.0.0/24", "172.29.1.0/24", "172.29.2.0/24"]
  private_subnets = ["172.29.10.0/24", "172.29.11.0/24", "172.29.12.0/24"]
  
  enable_nat_gateway  = true
  single_nat_gateway  = false
  one_nat_gateway_per_az = true

  reuse_nat_ips       = true               # <= Skip creation of EIPs for the NAT Gateways
  external_nat_ip_ids = aws_eip.nat.*.id   # <= IPs specified here as input to the module
}

resource "aws_eip" "nat" {
  count = 3

  vpc = true
}

※NAT Gateway に利用料が掛かるため、ハンズオンの終了後にterraform はdestroy しましょう。

 

リソースのデプロイ

  • Terraform の環境変数にクレデンシャルファイル、プロファイルの指定がない場合は、シェルの環境変数に以下を設定します。
    • export AWS_ACCESS_KEY_ID=アクセスキー
    • export AWS_SECRET_ACCESS_KEY=シークレットアクセスキー
    • export AWS_DEFAULT_REGION=リージョン(例:us-east-1)
  • terraform でリソースをデプロイします。main.tf のディレクトリで、以下のコマンドを実行します。terraform plan にエラーがなければ、terraform apply を実行します。
    • terraform init
    • terraform plan
    • terraform apply
  • 念のため、terraform init を実行する前に、下記のAWS CLI コマンドを実行して、デプロイ先となるAWSアカウントが正しいことを確認することをお勧めします。
    • aws sts get-caller-identity

 

$ export AWS_ACCESS_KEY_ID=アクセスキー
$ export AWS_SECRET_ACCESS_KEY=シークレットアクセスキー
$ export AWS_DEFAULT_REGION=リージョン


$ aws sts get-caller-identity
{
    "UserId": "AAAAAAAAAAAAAAAAAAAAA",
    "Account": "111111111111",
    "Arn": "arn:aws:iam::111111111111:user/niikawa"
}
$ terraform init
Initializing modules...
Downloading terraform-aws-modules/vpc/aws 3.12.0 for sample_vpc...
- sample_vpc in .terraform/modules/sample_vpc

Initializing the backend...

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

Initializing provider plugins...
- Finding hashicorp/aws versions matching ">= 3.63.*"...
- Installing hashicorp/aws v4.0.0...
- Installed hashicorp/aws v4.0.0 (self-signed, key ID 34365D9472D7468F)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/plugins/signing.html

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 plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:
*** 省略 ***

Plan: 28 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:
*** 省略 ***

Plan: 28 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

*** 省略 ***

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

 

  • AWSコンソールから、作成されたリソースを確認します。

 

 

 

 

ハンズオン2:Security Group作成

  • ハンズオン2は、ハンズオン1で作成したVPC内にSecurity Group を作成します。
  • インバウンドルールに下記の3つを登録します。
    • VPC CIDR 内からのHTTP (TCP/Port 80) の許可
    • MyOffice からのHTTP (TCP/Port 80) の許可
    • MyOffice からのSSH (TCP/Port 22) の許可
  • アウトバウンドルールに下記の1つを登録します。
    • Any (0.0.0.0/0) からのすべてのトラフィックを許可

 

tfコード作成

  • AWS EC2-VPC Security Group Terraform module にアクセスします。

 

  • Provision Instructions の記述をコピーし、main.tf に貼りつけます。

 

  • こちらのUsage のサンプルを参考にして、カスタムルールを使用したセキュリティグループを作成します。
  • サンプルでは、vpc_id に"vpc-12345678″が指定されていますが、ハンズオン1で作成したVPC のID を指定するため、VPC Module のvpc_id を記載します。
    • 例:module.sample_vpc.vpc_id
  • インバウンドルールのVPC CIDR には、"172.29.0.0/16″を直接指定するのではなく、ハンズオン1で作成したVPC のCIDR を指定するため、VPC Module のcidr_blocks を記載します。
    • 例:module.sample_vpc.vpc_cidr_block

 

  • こちらのUsage のサンプルを参考にすると、name に"niikawa-test-public-ec2″ を指定すると、セキュリティグループ名は"niikawa-test-public-ec2″ ではなく、"niikawa-test-public-ec2-20220212063215518500000001″ の様にタイムスタンプが付加されました。デフォルトでは、name はprefixとして扱われ、セキュリティグループ名は指定の文字列に固定されません。

  • セキュリティグループ名をname に指定した文字列に固定するには、"use_name_prefix = false" のパラメータを指定します。このパラメータを追加後は、下記の名前となりました。

 

  • サンプルコードを元に作成したコードは、以下となります。MyOffice のIPアドレスを xx.xx.xx.xx/32 としております。ご自分のPCのパブリックIP アドレスを記載ください。
module "public_ec2_security-group" {
  source  = "terraform-aws-modules/security-group/aws"

  use_name_prefix = false
  name        = "niikawa-test-public-ec2"
  description = "Security group for Public EC2"
  vpc_id      = module.sample_vpc.vpc_id

  ingress_with_cidr_blocks = [
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      description = "allow-http-80-ownvpc"
      cidr_blocks = module.sample_vpc.vpc_cidr_block
    },
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      description = "allow-http-80-myoffice"
      cidr_blocks = "xx.xx.xx.xx/32"
    },
    {
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      description = "allow-ssh-22-myoffice"
      cidr_blocks = "xx.xx.xx.xx/32"
    }
  ]
  egress_with_cidr_blocks = [
    {
      from_port   = 0
      to_port     = 0
      protocol    = "-1"
      description = ""
      cidr_blocks = "0.0.0.0/0"
    }
  ]
}

 

リソースのデプロイ

  • terraform init & plan & apply を行います。(Module を追加後は、terraform init が必要になります
  • AWSコンソールから、作成されたリソースを確認します。

 

 

ハンズオン3:Key Pair作成

  • ハンズオン3は、ハンズオン4のEC2作成で使用するKey Pair を作成します。

tfコード作成

  • AWS Key Pair Terraform module にアクセスします。

 

  • Provision Instructions の記述をコピーし、main.tf に貼りつけます。

 

  • こちらのUsage のサンプルを参考にして、秘密鍵・公開鍵を作成します。暗号化方式は、RSA を使用します。
  • EC2 のSSH login に秘密鍵が必要になるため、下記のoutput を追加して、秘密鍵・公開鍵の内容を取得できるようにします。
    • output “private_key_pem" {
      value = tls_private_key.keygen.private_key_pem
      }
    • output “public_key_pem" {
      value = tls_private_key.keygen.public_key_openssh
      }
  • サンプルコードを元に作成したコードは、以下となります。
resource "tls_private_key" "keygen" {
  algorithm = "RSA"
}

module "sample_key_pair" {
  source = "terraform-aws-modules/key-pair/aws"

  key_name   = "niikawa-test-key"
  public_key = tls_private_key.keygen.public_key_openssh
}

output "private_key_pem" {
  value = tls_private_key.keygen.private_key_pem
}

output "public_key_pem" {
  value = tls_private_key.keygen.public_key_openssh
}

 

リソースのデプロイ

  • terraform init & plan & apply を行います。(Module を追加後は、terraform init が必要になります
  • output によって、作成されたKey Pair の秘密鍵の内容が出力されました。

 

  • AWSコンソールから、作成されたリソースを確認します。

 

ハンズオン4:EC2作成

  • ハンズオン4は、EC2 を作成します。今回は、パブリックサブネットにインスタンスを配置します。
  • AMI には、Amazon Linux2 の最新イメージを利用します。
  • httpd サービスを起動させ、リソースをデプロイ後にApache のテストページが表示されることを確認します。

 

構成図

  • 以下、ハンズオン4の構成図です。

 

tfコード作成

  • AWS EC2 Instance Terraform module にアクセスします。

 

  • Provision Instructions の記述をコピーし、main.tf に貼りつけます。

 

  • AMIに最新のAmazon Linux 2 イメージを使用するため、下記の記述を追加します。
    • data “aws_ssm_parameter" “amzn2_ami_latest" {
      # Kernel 5.10
      name = “/aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2"
      # Kernel 4.14
      # name = “/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
      }

 

  • こちらのUsage のサンプルを参考にします。
  • ami は"ami-ebd02392″ ではなく、先ほど準備した最新のAmazon Linux 2 イメージを指定します。
    • 例:ami = data.aws_ssm_parameter.amzn2_ami_latest.value
  • key_name は、ハンズオン3で作成したKey Pair を指定します。
    • 例:key_name = module.sample_key_pair.key_pair_key_name
  • vpc_security_group_ids は、ハンズオン2で作成したSecurity Group を指定します。
    • 例:vpc_security_group_ids = [module.public_ec2_security-group.security_group_id]
  • subnet_id は、ハンズオン1で作成したサブネットを指定します。VPC Module で指定したpublic_subnets の0番目となります。
    • 例:module.sample_vpc.public_subnets[0]
  • 今回は、httpd サービスを起動させるため、user_data を指定します。

 

  • 補足です。vpc_security_group_ids に以下の指定を行うとエラーとなりました。
    • vpc_security_group_ids = module.public_ec2_security-group.security_group_id
  • vpc_security_group_ids は、リスト型[]のため、下記のように指定する必要があります。
    • vpc_security_group_ids = [module.public_ec2_security-group.security_group_id]

Error: Invalid value for module argument

on main.tf line 96, in module “sample_ec2-instance":
96: vpc_security_group_ids = module.public_ec2_security-group.security_group_id

The given value is not suitable for child module variable
“vpc_security_group_ids" defined at
.terraform/modules/sample_ec2-instance/variables.tf:223,1-34: list of string
required.

 

  • サンプルコードを元に作成したコードは、以下となります。
data "aws_ssm_parameter" "amzn2_ami_latest" {
  # Kernel 5.10
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2"
  #  Kernel 4.14
  #  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}

module "sample_ec2-instance" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  
  name = "niikawa-test-ec2"

  ami                    = data.aws_ssm_parameter.amzn2_ami_latest.value
  instance_type          = "t2.micro"
  key_name               = module.sample_key_pair.key_pair_key_name
  monitoring             = true
  vpc_security_group_ids = [module.public_ec2_security-group.security_group_id]
  subnet_id              = module.sample_vpc.public_subnets[0]

  user_data = <<EOF
    #!/bin/bash
    yum install -y httpd
    systemctl enable httpd.service
    systemctl start httpd.service
EOF
}

 

リソースのデプロイ

  • terraform init & plan & apply を行います。(Module を追加後は、terraform init が必要になります
  • AWSコンソールから、作成されたリソースを確認します。

 

  • ブラウザからインスタンスのパブリックIP にアクセスします。

 

  • ターミナルからEC2 にloginできることを確認します。ハンズオン3で作成した秘密鍵を使用します。

 

  • Terraform Registry を利用することで、Terraform Registry のModule とサンプルを使用して簡単に構築が行えました!

 

参考資料

 

  • クレデンシャルの設定方法をまとめています。

 

  • MFA導入環境でaws-mfa を使って認証を行う方法および aws-mfa でterraform を利用する際の補足を記載しています。