はじめに
AWSのVPCやサブネットなどのネットワークリソースを新規にTerraformで作成します。ネットワーク内でのローカル通信、外部からのアクセス、外部へのアクセスが可能なネットワークにします。
Terraformの基本的な使い方や、インストール方法などは、
を参考にしてみてください。
この先はTerraformがすでにインストールされている前提で進めます。
目次
実行環境
- Ubuntu:18.04
- Terraform: v0.13.4
作成するリソース
AWSにて新たにネットワーク環境を構築するにあたって必要になるリソースは、
- VPC:ネットワークアドレスを持った論理プライベートネットワーク
- サブネット:AZを跨いで配置するVPCを分割したネットワーク
- インターネットゲートウェイ:インターネットとVPC内を繋ぐためのサービス
- ルートテーブル:VPC内部やインターネットゲートウェイなどとのルーティングを設定する表
です。
それぞれをTerraformにて定義していきます。
各リソースをTFファイルとして作成
各リソースの書き方のルールについては、
を参考にできます。
VPC
まずはVPCを作成します。
resource "aws_vpc" "sample_vpc" { cidr_block = "10.10.0.0/16" # VPCに設定したいCIDRを指定 enable_dns_support = "true" # VPC内でDNSによる名前解決を有効化するかを指定 enable_dns_hostnames = "true" # VPC内インスタンスがDNSホスト名を取得するかを指定 instance_tenancy = "default" # VPC内インスタンスのテナント属性を指定 assign_generated_ipv6_cidr_block = "false" # IPv6を有効化するかを指定 tags = { Name = "sample_vpc" } }
必須設定はcidr_block
のみです。
上記では、任意の設定のうち、デフォルト値から変えていない設定もあえて記載しています。なぜ設定しているかと言うと、Infrastructure as Codeとしてインフラの設定をコードとして残しておきたい場合は、デフォルトの設定でも必要なものは明記しておいた方が賢明だからです。「デフォルト値で良いと判断して何も書かなかった場合」と「その任意設定を知らない(意識していない)ことで何も書かなかった場合」の混在を避けることができるためです。
なお、instance_tenancy
は、VPC内で起動するインスタンスのテナント属性を設定できます。テナント属性の種類は下記のように3種類あります。
さらなる詳細は、Terraform Registry AWS VPCを参考にしてください。
サブネット
次にサブネットを作成します。
resource "aws_subnet" "sample_subnet_a" { vpc_id = aws_vpc.sample_vpc.id # VPCのIDを指定 cidr_block = "10.10.1.0/24" # サブネットに設定したいCIDRを指定 assign_ipv6_address_on_creation = "false" # IPv6を利用するかどうかを指定 map_public_ip_on_launch = "true" # VPC内インスタンスにパブリックIPアドレスを付与するかを指定 availability_zone = "ap-northeast-1a" # サブネットが稼働するAZを指定 tags = { Name = "sample_subnet_1a" } } resource "aws_subnet" "sample_subnet_c" { vpc_id = aws_vpc.sample_vpc.id cidr_block = "10.10.1.0/24" assign_ipv6_address_on_creation = "false" map_public_ip_on_launch = "true" availability_zone = "ap-northeast-1c" tags = { Name = "sample_subnet_1c" } }
2つのAZそれぞれにサブネットを作成する内容にしています。
必須設定はvpc_id
とcidr_block
です。
インターネットゲートウェイ
続いてインターネットゲートウェイを作成します。
resource "aws_internet_gateway" "sample_igw" { vpc_id = aws_vpc.sample_vpc.id # VPCのIDを指定 tags = { Name = "sample_igw" } }
必須設定はvpc_id
のみです。
ルートテーブル
さて、VPC/サブネットとインターネットゲートウェイを作成したので、それぞれでルーティングできるようにルートテーブルと、紐付け設定を作成していきます。
まずはルートテーブルです。ルートテーブルは「送信されたパケットの宛先IPアドレスを確認し、どこに通信を流すかを判断する時に使う表」です。
そのため、ルートの設定には、宛先IPアドレスとターゲットの属性(local、gateway、特定のEC2、など)を指定する必要があります。以下では、外部向け通信を可能にするためのルート設定を加えています。
resource "aws_route_table" "sample_rt" { vpc_id = aws_vpc.sample_vpc.id # VPCのIDを指定 # 外部向け通信を可能にするためのルート設定 route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.sample_igw.id } tags = { Name = "sample_rt" } }
なお、サブネット内での通信を可能にするためのルートの設定も必要ですが、これは自動的に作成されるので、設定していません。
ルートテーブルを作成したので、実際のVPCやサブネットと紐づける設定を作成します。
resource "aws_main_route_table_association" "sample_rt_vpc" { vpc_id = aws_vpc.sample_vpc.id # 紐づけたいVPCのIDを指定 route_table_id = aws_route_table.sample_rt.id # 紐付けたいルートテーブルのIDを指定 } resource "aws_route_table_association" "sample_rt_subet_a" { subnet_id = aws_subnet.sample_subnet_a.id # 紐づけたいサブネットのIDを指定 route_table_id = aws_route_table.sample_rt.id # 紐付けたいルートテーブルのIDを指定 } resource "aws_route_table_association" "sample_rt_subnet_c" { subnet_id = aws_subnet.sample_subnet_c.id route_table_id = aws_route_table.sample_rt.id }
それぞれ、VPC本体、サブネット2つとルートテーブルを紐づけています。
作成したTFファイルを適用して実際にAWSリソースを作成する
ここまでの設定を全て書いたnetworks.tf
というファイルを用意します。
また以下の内容でproviders.tf
を作成し、AWSへのリソース作成であることを明示します。
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 3.10.0" } } } provider "aws" { region = "ap-northeast-1" }
これらのファイルがあるディレクトリで、以下のコマンドを実行し、AWSリソースを作成します。(アクセスしたいAWSアカウントのconfigureの設定は完了している前提です。)
terraform init # 初期化 terraform plan # 設定が正しいかを事前確認 terraform apply # 適用
以下のメッセージが出れば問題なく設定完了です。
Apply complete! Resources: 8 added, 0 changed, 0 destroyed.
AWSのコンソールを確認することで、設定した通りのVPCやルートテーブルが作成されていることがわかると思います。
別途、作成したVPCを紐付けたEC2を作成することで、sshによるログインや外部からのライブラリのインストールなどが可能なことを確認できます。
環境変数およびmoduleを利用して扱いやすくする
さて、以上で問題なく新たなネットワークを作成できましたが、ここではTFファイルをさらに扱いやすくするため、環境変数化とmodule化をしていきます。
環境変数化
環境変数を利用してTFファイルを再利用可能にします。
環境変数を利用するには、variables.tf
をいうファイルで環境変数を定義しておく必要があります。
今回は、VPC/サブネットのCIDRと環境(devかprodか)を環境変数として設定してみました。
以下はvariables.tf
の例です。
variable ENV { type = string description = "環境(prod/dev)" default = "dev" } variable VPC_CIDR { type = string description = "VPCのCIDR" default = "10.0.0.0/16" } variable SUBNET_A_CIDR { type = string description = "subnet aのCIDR" default = "10.0.1.0/24" } variable SUBNET_C_CIDR { type = string description = "subnet cのCIDR" default = "10.0.2.0/24" }
また、networks.tf
を以下のように変更します。
変更点としては、各所で環境変数を利用した点と、tagにENVの情報を付与した点です。
resource "aws_vpc" "sample_vpc" { cidr_block = var.VPC_CIDR # VPCに設定したいCIDRを指定 enable_dns_support = "true" # VPC内でDNSによる名前解決を有効化するかを指定 enable_dns_hostnames = "true" # VPC内インスタンスがDNSホスト名を取得するかを指定 instance_tenancy = "default" # VPC内インスタンスのテナント属性を指定 assign_generated_ipv6_cidr_block = "false" # IPv6を有効化するかを指定 tags = { Name = "sample_vpc_${var.ENV}" Env = var.ENV } } resource "aws_subnet" "sample_subnet_a" { vpc_id = aws_vpc.sample_vpc.id # VPCのIDを指定 cidr_block = var.SUBNET_A_CIDR # サブネットに設定したいCIDRを指定 assign_ipv6_address_on_creation = "false" # IPv6を利用するかどうかを指定 map_public_ip_on_launch = "true" # VPC内インスタンスにパブリックIPアドレスを付与するかを指定 availability_zone = "ap-northeast-1a" # サブネットが稼働するAZを指定 tags = { Name = "sample_subnet_${var.ENV}_1a" Env = var.ENV } } resource "aws_subnet" "sample_subnet_c" { vpc_id = aws_vpc.sample_vpc.id cidr_block = var.SUBNET_C_CIDR assign_ipv6_address_on_creation = "false" map_public_ip_on_launch = "true" availability_zone = "ap-northeast-1c" tags = { Name = "sample_subnet_${var.ENV}_1c" Env = var.ENV } } resource "aws_internet_gateway" "sample_igw" { vpc_id = aws_vpc.sample_vpc.id # VPCのIDを指定 tags = { Name = "sample_igw_${var.ENV}" Env = var.ENV } } resource "aws_route_table" "sample_rt" { vpc_id = aws_vpc.sample_vpc.id # VPCのIDを指定 # 外部向け通信を可能にするためのルート設定 route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.sample_igw.id } tags = { Name = "sample_rt_${var.ENV}" Env = var.ENV } } resource "aws_main_route_table_association" "sample_rt_vpc" { vpc_id = aws_vpc.sample_vpc.id # 紐づけたいVPCのIDを指定 route_table_id = aws_route_table.sample_rt.id # 紐付けたいルートテーブルのIDを指定 } resource "aws_route_table_association" "sample_rt_subet_a" { subnet_id = aws_subnet.sample_subnet_a.id # 紐づけたいサブネットのIDを指定 route_table_id = aws_route_table.sample_rt.id # 紐付けたいルートテーブルのIDを指定 } resource "aws_route_table_association" "sample_rt_subnet_c" { subnet_id = aws_subnet.sample_subnet_c.id route_table_id = aws_route_table.sample_rt.id }
これでterraform apply
時に自動的にvariables.tf
の中身を読み込み、デフォルト値に設定した環境変数が反映されます。
module化
TFファイルをさらに共有して使いやすくするために、module化します。
以下のようなディレクトリ構成を作り、各ファイルを格納します。
├── modules │ ├── networks.tf │ ├── provider.tf │ └── variables.tf └── services └── dev └── main.tf
main.tf
には環境変数とともに実行するソースモジュールを指定します。
module "samples" { source = "../../modules/" ENV = "dev" VPC_CIDR = "10.10.0.0/16" SUBNET_A_CIDR = "10.10.1.0/24" SUBNET_C_CIDR = "10.10.2.0/24" }
この状態で、services/dev
ディレクトリでapplyすることで、main.tf
がmodulesの全てのファイルを読み込み、AWSリソースを作成してくれます。
cd services/dev
terraform init
terraform apply
以上のようにmodule化することでmain.tf
の中身を変えるだけで様々な設定のネットワークを作成することができるようになります。