LoginSignup
0

Step Functionsを利用してNAT Gatewayを自動作成/削除する

Last updated at Posted at 2023-12-29

概要

本記事ではStep Functionsを利用して、Nat Gatewayを自動で作成/削除する方法について記載します。
NAT Gatewayは作成しているだけでコストがかかるリソースであり、開発環境の利用していない時間帯などは停止(削除)することでコスト削減につながります。

同じことを以下の記事でCloudFormationを利用して説明していますが、今回はStep Functionsを利用して実施したいと思います。
https://fu3ak1.hatenablog.com/entry/2020/07/28/135924

以下のことをStep Functionsを利用して実現します。

  • 作成時
    • 既に存在するElastic IPとSubnetを指定してNAT Gatewayを作成する。
    • ルートテーブルを更新し、デフォルトゲートウェイ(0.0.0.0/0)のルーティング先をNAT Gatewayにする
  • 削除時
    • NAT Gatewayを削除する
    • ルートテーブルからデフォルトゲートウェイ(0.0.0.0/0)のルーティングを削除する

Nat Gatewayを作成する

Step FunctionsでNat Gatewayを作成するために必要な情報をJSONの入力で受け取ります。

Step Functionsの入力は以下になります。

{
  "subnetId": "subnet-xxxx",
  "allocationId": "eipalloc-xxxx",
  "routeTableId": "rtb-xxxx"
}
  • subnetId: NatGWを作成したいSubnetを指定する
  • allocationId: ElasticIPの割り当てIP(allocationId)を指定する。NatGWで利用するElastic IPは固定IPとして利用することを想定して、既に存在するIPを指定します。
  • routeTableId: NatGWをデフォルトのルートとするルートテーブルを指定します。

Step Functionsの定義は以下になります。

スクリーンショット 2023-12-29 23.08.08.png

  • CreateNatGateway SubnetIdとAllocationIdを指定してNatGWを作成します。
  • Wait NatGWが利用可能になるまで、ルートテーブルのルートに指定できないため、30秒待機します。
  • DescribeNatGateways 30秒待機した後、NatGWの情報を取得します。
  • CheckStatus NatGWがavailableならば、ルートテーブルにルートを追加するステップに遷移します。まだ、利用可能でない場合はWaitに遷移して、30秒待機します。
  • CreateRoute ルートテーブルにルートを追加します。
ワークフロー定義

{
  "StartAt": "CreateNatGateway",
  "States": {
    "CreateNatGateway": {
      "Type": "Task",
      "Parameters": {
        "SubnetId.$": "$.subnetId",
        "AllocationId.$": "$.allocationId"
      },
      "Resource": "arn:aws:states:::aws-sdk:ec2:createNatGateway",
      "Next": "DescribeNatGateways",
      "ResultSelector": {
        "NatGatewayId.$": "$.NatGateway.NatGatewayId"
      },
      "ResultPath": "$.NatGateway"
    },
    "Wait": {
      "Type": "Wait",
      "Seconds" : 30,
      "Next": "DescribeNatGateways"
    },
    "DescribeNatGateways": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:ec2:describeNatGateways",
      "Parameters": {
        "Filter": [
          {
            "Name": "nat-gateway-id",
            "Values.$": "States.Array($.NatGateway.NatGatewayId)"
          }
        ]
      },
      "Next": "CheckStatus",
      "ResultSelector": {
        "NatGatewayId.$": "$.NatGateways[0].NatGatewayId",
        "State.$": "$.NatGateways[0].State"
      },
      "ResultPath": "$.NatGateway"
    },
    "CheckStatus": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.NatGateway.State",
          "StringEquals": "available",
          "Next": "CreateRoute"
        }
      ],
      "Default": "Wait"
    },
    "CreateRoute": {
      "Type": "Task",
      "Next": "Succeed",
      "Parameters": {
        "DestinationCidrBlock": "0.0.0.0/0",
        "NatGatewayId.$": "$.NatGateway.NatGatewayId",
        "RouteTableId.$": "$.routeTableId"
      },
      "Resource": "arn:aws:states:::aws-sdk:ec2:createRoute"
    },
    "Succeed": {
      "Type": "Succeed"
    }
  }
}

Nat Gatewayを削除する

Step FunctionsでNat Gatewayを削除するために必要な情報をjsonの「入力」で受け取ります。

Step Functionsの入力は以下になります。

  • subnetId: 削除したいNat Gatewayが存在するSubnetを指定します。
  • routeTableId: デフォルトルートにNat Gatewayのルートテーブルを指定します。
{
  "subnetId": "subnet-xxxx",
  "routeTableId": "rtb-xxxx"
}

Step Functionsの定義は以下になります。

  • DescribeNatGateways: 指定したSubnetに存在するNat Gatewayを取得します。後続でそのIdを指定します。作成削除を行う上で、Nat GatewayのIdは固定されていません。そのため、固定のリソースである、SubnetからDescribeしてIdを取得します。
  • DeleteNatGateway: DescribeNatGatewaysで取得したNat Gatewayをidを指定して削除します。
  • DeleteRoute: ルートテーブルに存在するデフォルトルートを削除します。

スクリーンショット 2023-12-29 23.18.39.png

ワークフロー定義
{
  "StartAt": "DescribeNatGateways",
  "States": {
    "DescribeNatGateways": {
      "Type": "Task",
      "Next": "DeleteNatGateway",
      "Parameters": {
        "Filter": [
          {
            "Name": "subnet-id",
            "Values.$": "States.Array($.subnetId)"
          },
          {
            "Name": "state",
            "Values": ["available"]
          }
        ]
      },
      "Resource": "arn:aws:states:::aws-sdk:ec2:describeNatGateways",
      "ResultPath": "$.DescribeNatGateways",
      "Next": "DeleteNatGateway"
    },
    "DeleteNatGateway": {
      "Type": "Task",
      "Parameters": {
        "NatGatewayId.$": "$.DescribeNatGateways.NatGateways[0].NatGatewayId"
      },
      "Resource": "arn:aws:states:::aws-sdk:ec2:deleteNatGateway",
      "ResultPath": "$.DeleteNatGateway",
      "Next": "DeleteRoute"
    },
    "DeleteRoute": {
      "Type": "Task",
      "Next": "Succeed",
      "Parameters": {
        "DestinationCidrBlock": "0.0.0.0/0",
        "RouteTableId.$": "$.routeTableId"
      },
      "Resource": "arn:aws:states:::aws-sdk:ec2:deleteRoute"
    },
    "Succeed": {
      "Type": "Succeed"
    }
  }
}

複数のNAT Gatewayを作成する

上記の例では一つのNAT Gatewayを指定するパターンを紹介しましたが、複数指定できる方が望ましいです。
また、Step Functionsの入力はいちいちStep Functions実行時に指定するのではなく、どこかで固定しておける方が良いと思います。
そこで、今回は入力をStep FunctionsのItemReaderで取得し、Mapで複数処理することにします。
ItemReaderによってS3に保存されているJSONから複数の入力を受け取ることができます。
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/input-output-itemreader.html

NAT Gatewayを複数作成する例を紹介します。削除する方法も同様の方法で実現できます。

Step Functionsの定義は以下のようになります。createNatGatewayから始まる一連の処理は同じですが、入力をS3から取得して、複数のワークフローを実行できるようにしています。

スクリーンショット 2023-12-30 0.04.34.png

ワークフロー定義
{
  "StartAt": "MapNATGWFromS3",
  "States": {
    "MapNATGWFromS3": {
      "Type": "Map",
      "ItemReader": {
        "Resource": "arn:aws:states:::s3:getObject",
        "ReaderConfig": {
          "InputType": "JSON"
        },
        "Parameters": {
            "Bucket": "{S3バケットを指定}",
            "Key": "create-natgw.json"
        }
      },
      "MaxConcurrency": 1000,
      "Label": "MapNATGWFromS3",
      "End": true,
      "ItemProcessor": {
        "ProcessorConfig": {
          "Mode": "DISTRIBUTED",
          "ExecutionType": "STANDARD"
        },
        "StartAt": "createNatGateway",
        "States": {
          "createNatGateway": {
            "Type": "Task",
            "Parameters": {
              "SubnetId.$": "$.subnetId",
              "AllocationId.$": "$.allocationId"
            },
            "Resource": "arn:aws:states:::aws-sdk:ec2:createNatGateway",
            "Next": "CheckStatus",
            "ResultSelector": {
              "NatGatewayId.$": "$.NatGateway.NatGatewayId"
            },
            "ResultPath": "$.NatGateway"
          },
          "Wait": {
            "Type": "Wait",
            "Seconds" : 30,
            "Next": "CheckStatus"
          },
          "CheckStatus": {
            "Type": "Task",
            "Resource": "arn:aws:states:::aws-sdk:ec2:describeNatGateways",
            "Parameters": {
              "Filter": [
                {
                  "Name": "nat-gateway-id",
                  "Values.$": "States.Array($.NatGateway.NatGatewayId)"
                }
              ]
            },
            "Next": "IsRunning",
            "ResultSelector": {
              "NatGatewayId.$": "$.NatGateways[0].NatGatewayId",
              "State.$": "$.NatGateways[0].State"
            },
            "ResultPath": "$.NatGateway"
          },
          "IsRunning": {
            "Type": "Choice",
            "Choices": [
              {
                "Variable": "$.NatGateway.State",
                "StringEquals": "available",
                "Next": "CreateRoute"
              }
            ],
            "Default": "Wait"
          },
          "CreateRoute": {
            "Type": "Task",
            "Next": "Succeed",
            "Parameters": {
              "DestinationCidrBlock": "0.0.0.0/0",
              "NatGatewayId.$": "$.NatGateway.NatGatewayId",
              "RouteTableId.$": "$.routeTableId"
            },
            "Resource": "arn:aws:states:::aws-sdk:ec2:createRoute"
          },
          "Succeed": {
            "Type": "Succeed"
          }
        }
      }
    }
  }
}

ワークフローの最初で指定しているcreate-natgw.jsonは以下のようなJSONになります。
NAT Gatewayを作成したい[subnetId, allocationId, routeTableId]の組を指定します。
これを指定したS3に保存しておくことで、Step Functionsの入力として処理することができます。

[
  {
  "subnetId": "subnet-xxxx",
  "allocationId": "eipalloc-xxxx",
  "routeTableId": "rtb-xxxx"
  },
  {
    "subnetId": "subnet-yyyy",
    "allocationId": "eipalloc-yyyy",
    "routeTableId": "rtb-yyyy"
  }
]

EventBridgeを利用して定期的に実行する

上記で作成したStep Functionを定期的に実行することで、作成/削除を行うことができます。
terraformのコードで例えば9時に作成して、20時に削除する場合は以下のようになります。

resource "aws_cloudwatch_event_rule" "create" {
  name        = "create-rule"
  description = "create rule"

  is_enabled          = true
  schedule_expression = "cron(0 0 * * ? *)"
}

resource "aws_cloudwatch_event_target" "create" {
  target_id = "create-rule"
  arn       = "{Nat Gatewayを作成するstepfunctionsのarn}"
  rule      = aws_cloudwatch_event_rule.stop.name
  role_arn  = aws_iam_role.eventbridge.arn
}

resource "aws_cloudwatch_event_rule" "delete" {
  name        = "delete-rule"
  description = "delete rule"

  is_enabled          = true
  schedule_expression = "cron(0 11 * * ? *)"
}

resource "aws_cloudwatch_event_target" "delete" {
  target_id = "delete-rule"
  arn       = "{Nat Gatewayを削除するstepfunctionsのarn}"
  rule      = aws_cloudwatch_event_rule.stop.name
  role_arn  = aws_iam_role.eventbridge.arn
}

まとめ

Step Functionsを利用してNAT Gatewayを定期的に作成/削除する方法を紹介しました。
NAT Gatewayは利用しない時間もコストがかかるサービスなので、少なからずコスト削減につながると思います。

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