1
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 1 year has passed since last update.

【Terraform】AWS Security Group構築用のmoduleを作成してみた

Last updated at Posted at 2023-06-23

はじめに

セキュリティグループって作るの面倒ですよね。
開発・検証・本番と環境が増えるごとにより面倒に。。

インフラ構築をコード化出来たらなー、ということで Terraform の module を作ってみました。
どうせ作ったなら皆さんの参考になればということで共有します。

バージョンは以下の通り。
このバージョン以外での動作は確認してません。

terraform {
  required_version = "1.5.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.3.0"
    }
  }
}

GitHubにソースコードを上げておりますので、もしよろしければ参考に。
こちらをクリック

参考にしていただくためにGitHubに残しております。
いかなる損失が発生しても一切責任は負いませんのでよろしくお願いしますね。

module

セキュリティグループ構築用の module について軽く説明します。
ややこしいことは何もやってないです。

一部抜粋して説明していきます。

variables.tf

まずは変数ファイルから。ものすごくシンプル・・・!

module/security_group/variables.tf
#
# aws_security_group
#
variable "description" {
  type        = string
  default     = ""
  description = "セキュリティグループの説明"
}
variable "vpc_id" {
  type        = string
  default     = null
  description = "セキュリティグループを作成するVPCのID。デフォルトはリージョンのデフォルトVPC"
}

#
# aws_security_group_rule
#
variable "rule_map" {    ※1
  type = map(object({
    type                     = string       # ingress or egress
    from_port                = number       # 開始ポート
    to_port                  = number       # 終了ポート
    protocol                 = string       # プロトコル
    cidr_blocks              = list(string) # IPv4 CIDRブロックのリスト
    ipv6_cidr_blocks         = list(string) # IPv6 CIDRブロックのリスト
    source_security_group_id = string       # 送信元セキュリティグループID
    description              = string       # ルールの説明
  }))
  default     = {}
  description = "セキュリティグループルールのマップ"
}

※1

variable "rule_map" {・・・}

変数の中で一番ややこしいのがこれ。
map 型で記述したセキュリティグループのルールをこの変数に代入します。
map についての公式ページは こちら

変数 rule_map の型は map(object({・・・})) なので、
map の value の部分を object型 にする必要があります。
なんか説明だとややこしいので後ほどコードを用いて説明します。。

outputs.tf

特に難しいことはしてないです。
セキュリティグループのIDと名前を返り値にしてます。

module/security_group/outputs.tf
output "sg_id" {
  value       = aws_security_group.this.id
  description = "セキュリティグループのID"
}
output "sg_name" {
  value       = aws_security_group.this.name
  description = "セキュリティグループ名"
}

main.tf

aws_security_group_rule リソースが少しややこしいです。

module/security_group/main.tf
#
# Security Group
#
resource "aws_security_group" "this" {
  name        = local.sg_name
  description = var.description
  vpc_id      = var.vpc_id

  tags = {
    Name = local.sg_name
  }
}

#
# Security Group Rule
#
resource "aws_security_group_rule" "this" {
  for_each = var.rule_map

  security_group_id        = aws_security_group.this.id
  type                     = each.value.type
  from_port                = each.value.from_port
  to_port                  = each.value.to_port
  protocol                 = each.value.protocol
  cidr_blocks              = each.value.cidr_blocks     ※1
  ipv6_cidr_blocks         = each.value.ipv6_cidr_blocks
  source_security_group_id = each.value.source_security_group_id
  description              = each.value.description
}

※1

cidr_blocksipv6_cidr_blockssource_security_group_idのうち、
設定するパラメータは一つのみとなります。

つまり、cidr_blocks10.0.0.0/16などを指定した場合は、残りの2つのパラメータは設定できません。
※ 複数個を同時に設定しようとするとエラーになります。

上記3つのパラメータは、送信元を指定するパラメータなので当然ですね。

module 呼び出し元

module 呼び出し元は、今回サンプルとして2種類のセキュリティグループを作成してます。
1つ目は、IPv4からの通信を許可する場合のセキュリティグループ。
2つ目は、セキュリティグループからの通信許可やアウトバウンドルールを設定したセキュリティグループ。

まずは 1つ目の sample-sg-1 から。

network.tf
module "sample_sg_1" {
  source = "./modules/security_group"

  prefix  = local.prefix
  subname = "sample-sg-1"

  # Security Group
  description = "sample security group 1"

  # security Group Rule
  rule_map = {
    http = {
      type                     = "ingress"
      from_port                = 80
      to_port                  = 80
      protocol                 = "TCP"
      cidr_blocks              = ["0.0.0.0/0"]    ※1
      ipv6_cidr_blocks         = null      ※2
      source_security_group_id = null      ※3
      description              = "allow http"
    }
  }
}

※1

cidr_blocks = ["0.0.0.0/0"]

list(string)型で渡す必要があるので、上記のような記述方法になります。

※2

ipv6_cidr_blocks = null

main.tf の※1で説明した通り、3つのパラメータの内の1つしか設定できないため、
設定対象ではないパラメータについては null を渡します。

※3

source_security_group_id = null

network.tf の ※2と同様


続いては2つ目のセキュリティグループです。

network.tf
module "sample_sg_2" {
  source = "./modules/security_group"

  prefix  = local.prefix
  subname = "sample-sg-2"

  # Security Group
  description = "sample security group 2"

  # Security Group Rule
  rule_map = {
    from_sample = {
      type                     = "ingress"
      from_port                = 0     ※1
      to_port                  = 0
      protocol                 = -1    ※2
      cidr_blocks              = null
      ipv6_cidr_blocks         = null
      source_security_group_id = module.sample_sg_1.sg_id    ※3
      description              = "allow all traffic from ${module.sample_sg_1.sg_name}"   ※4
    }
    to_sample = {
      type                     = "egress"    ※5
      from_port                = 0
      to_port                  = 65535       ※6
      protocol                 = "TCP"
      cidr_blocks              = null
      ipv6_cidr_blocks         = null
      source_security_group_id = module.sample_sg_1.sg_id
      description              = "allow tcp to ${module.sample_sg_1.sg_name}"
    }
  }
}

※1

from_port = 0

ポート範囲をすべてにする場合は、from_port と to_port を0にします。

※2

protocol = -1

プロトコルをすべてにする場合は、protocol を-1にします。

※3

source_security_group_id = module.sample_sg_1.sg_id

送信元にセキュリティグループIDを設定しています。
sample_sg_1 の sg_id(セキュリティグループID)を指定。

しつこいですがこの場合、cidr_blocksipv6_cidr_blocksは設定できないので null

※4

description = "allow all traffic from ${module.sample_sg_1.sg_name}"

module.sample_sg_1.sg_name でセキュリティグループ名を取得
※3と似たような感じです。

※5

type = "egress"

egress はアウトバウンドルール

※6

to_port = 65535

from_port と to_port が異なる数字の場合は、その範囲のポート番号が許可されます。
from_port = 0 、 to_port = 65535 の場合は0 - 65535

AWS リソース

一つ目のセキュリティグループ。

インバウンドルールは 0.0.0.0/0 からの HTTP:80 の通信を許可しているので想定通り。
image.png

アウトバウンドルールは設定していないので空でOK
image.png

二つ目のセキュリティグループ。

インバウンドルールは、sample_sg_1 のセキュリティグループからのすべての通信を許可。
ソース ・ 説明 ともに上手く参照して設定できている。

マネコンから一つずつ作成した場合、送信元のセキュリティグループIDが変わるたびに
インバウンドルールのソースを設定しなおさないといけないが、Terraformだとすべて良きようにやってくれるので凄く楽です。
image.png

アウトバウンドルールは、しっかりポートを範囲指定出来ているのでOKそう。
image.png

さいごに

最後までご覧いただきありがとうございました。
一度 module を作ってしまえばその後はとても楽なので、ぜひ皆さんも module を活用しましょう!

特にセキュリティグループやIAMロールなどは数が構築する数が多いのでコード化するとかなり楽です。
それにコード化されたインフラは冪等性があるのでオススメ。

今後もGitHubは更新していく予定なので役に立ったら記事にコメントでもしてくれると嬉しくて泣きます。
module の変更案などもコメントしてください。

では、良いTerraformライフをお送りください。

1
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
1
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?