📝
CloudFormation での VPC 作成時に CIDR が一意になるようにデプロイしてみた
カスタムリソースの Lambda で実現してみました。
1. Lambda 関数の作成
以下の設定で作成しました。
- ランタイム: Python 3.13
- IAM ロール: AdministratorAccess 権限を付与
- コード: 以下の通り
import json
import boto3
import urllib3
from ipaddress import ip_network
ec2 = boto3.client('ec2')
http = urllib3.PoolManager()
# 使用可能なCIDR候補リスト
AVAILABLE_CIDRS = [
'10.0.0.0/16',
'10.1.0.0/16',
'10.2.0.0/16',
'10.3.0.0/16',
'10.4.0.0/16'
]
def lambda_handler(event, context):
print(f"Received event: {json.dumps(event)}")
response_data = {}
response_status = 'SUCCESS'
reason = None
try:
if event['RequestType'] == 'Delete':
send_response(event, context, 'SUCCESS', {})
return
# 既存のVPCのCIDRを取得
response = ec2.describe_vpcs()
existing_cidrs = [vpc['CidrBlock'] for vpc in response['Vpcs']]
print(f"Existing VPC CIDRs: {existing_cidrs}")
print(f"Available CIDR candidates: {AVAILABLE_CIDRS}")
# 既存CIDRと重複しない候補を探す
available_cidr = None
for candidate_cidr in AVAILABLE_CIDRS:
candidate_network = ip_network(candidate_cidr)
is_overlap = False
for existing_cidr in existing_cidrs:
existing_network = ip_network(existing_cidr)
if candidate_network.overlaps(existing_network):
is_overlap = True
print(f"{candidate_cidr} overlaps with {existing_cidr}")
break
if not is_overlap:
available_cidr = candidate_cidr
print(f"Found available CIDR: {available_cidr}")
break
if available_cidr:
response_data = {
'AvailableCidr': available_cidr,
'ExistingCidrs': ','.join(existing_cidrs),
'CheckedCandidates': ','.join(AVAILABLE_CIDRS)
}
else:
response_status = 'FAILED'
reason = f"All CIDR candidates overlap with existing VPCs. Existing: {existing_cidrs}"
response_data = {
'ExistingCidrs': ','.join(existing_cidrs),
'CheckedCandidates': ','.join(AVAILABLE_CIDRS)
}
except Exception as e:
print(f"Error: {str(e)}")
response_status = 'FAILED'
reason = str(e)
send_response(event, context, response_status, response_data, reason)
def send_response(event, context, status, response_data, reason=None):
response_body = {
'Status': status,
'Reason': reason or f'See CloudWatch Log Stream: {context.log_stream_name}',
'PhysicalResourceId': context.log_stream_name,
'StackId': event['StackId'],
'RequestId': event['RequestId'],
'LogicalResourceId': event['LogicalResourceId'],
'Data': response_data
}
print(f"Response body: {json.dumps(response_body)}")
json_response = json.dumps(response_body).encode('utf-8')
headers = {'Content-Type': 'application/json'}
http.request('PUT', event['ResponseURL'], body=json_response, headers=headers)
今回は使用可能な VPC CIDR を配列として保持していますが、必要に応じてデータベースや Systems Manager Parameter Store に保存する方法も考えられます。
2. CloudFormation テンプレートの作成
最小構成などで Lambda 関数の ARM はハードコーディングしています。
AWSTemplateFormatVersion: "2010-09-09"
Description: "VPC with CIDR overlap check using Custom Resource"
Resources:
CidrChecker:
Type: Custom::CidrChecker
Properties:
ServiceToken: arn:aws:lambda:ap-northeast-1:012345678901:function:test
MyVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !GetAtt CidrChecker.AvailableCidr
EnableDnsHostnames: true
EnableDnsSupport: true
3. 既存 VPC の CIDR を確認
既存 VPC の CIDR は以下の通りです。
- 10.0.0.0/16
- 172.31.0.0/16

4. デプロイ
手順 2 のテンプレートをデプロイします。
デプロイ完了後、既存 VPC の CIDR とは別の CIDR で VPC が作成されていれば OK です。

Lambda の実行ログには以下のように CIDR チェックを実行した記録が残っています。
Existing VPC CIDRs: ['10.0.0.0/16', '172.31.0.0/16']
Available CIDR candidates: ['10.0.0.0/16', '10.1.0.0/16', '10.2.0.0/16', '10.3.0.0/16', '10.4.0.0/16']
10.0.0.0/16 overlaps with 10.0.0.0/16
Found available CIDR: 10.1.0.0/16
まとめ
今回は CloudFormation での VPC 作成時に CIDR が一意になるようにデプロイしてみました。
どなたかの参考になれば幸いです。
Discussion