
ネットワーク構成図をAWS CLIの出力内容から draw.ioで自動作成してみた (Claude Skill 化)
はじめに
ネットワーク構成図の手書き作業卒業を目標に色々やってみました。
ポイントとしては以下の3点です。
- 実環境から情報を取るための参照系のみのシェルを用意する
- 取得結果から、AWS 公式アイコンを使った draw.io (
.drawio) の構成図を作る - この作図の手順を Claude の Skill にして、次回以降も同じ品質で再現できるようにする
今まで苦労していたネットワークの構成整理にかなり役立つのではないかと思います。
この記事では、Skill に何を入れたか、事前情報取得シェルについて、そして実際に描けた構成図を紹介します。
どんな感じにできるのか
最初に出力結果です。
Claude Sonnet 5.5 で実行すると、下の図のような形で左に構成図、右にその説明というネットワークの構成図が作成されます。
右の説明が不要なら drawio のアプリで開いて編集するようにしてください。
下記の図から「NAT ゲートウェイが削除されていて、プライベートサブネットのリソースからインターネットに接続できない」「AZ とリソース名がずれている」ということがわかります。

全体の流れ
やっていることとしては以下の 5 点です。
- 参照系シェルを実行 (お客様環境の AWS CLI) → tar.gz にまとまる
- tar.gz を Claude に渡す
- Claude が棚卸し (数の突き合わせ・公開/非公開の判定)
- drawio を生成 → 検証スクリプトでチェック
- draw.io で開いて確認 → 修正依頼 → 版を上げて再作成
ポイントは、AWS へ直接触るのはシェルのみで参照系 API だけにしている点です。
Claude 側には AWS の認証情報を渡さず、出力された JSON だけを渡します。
Claude に認証情報を渡すのはまだ抵抗があるので AWS CloudShell などの環境でシェルを実行して、出力結果の tar.gz を Claude に渡す形式にしています。
Skill の作成のためにした壁打ちの内容
最後に補足として現状の Skill の内容を記載していますが、Skill を作成するにあたって壁打ちした内容は以下のみです。
- AWS の構成図を作りたい。 最新のアイコンがある URL を教えて。
- Claude が出力した URL(https://aws.amazon.com/architecture/icons/ )を使って、これから draw.io で構成図を作りたい。
- VPC のネットワーク構成図を作成したい。 どのようなリソース情報が必要ですか。
- 構成図の情報を取得するための AWS CLI をシェルにして教えて。
- 認証情報を記載しないようにして。
- シェルも Skill の中に組み込んで、実行用のシェルが欲しいと言われたら、共有するようにして。
- 横長で作成して
- 凡例の説明はテキストボックスではなくて実際に矢印アイコンを使用して簡略化して。
出力結果を調整しながら「Skill 化して」「Skill を更新して」みたいな形で補足の内容のような状態になりました。
情報取得用のシェル
設計方針
お客様の環境でも実行する想定のため、安心して実行できることを最優先にしました。
- 参照系 API (describe / list) だけを使い、リソースは変更しない。スクリプトの冒頭にもその旨を明記している
- 認証情報を取得しない。実行後に認証情報らしき文字列が出力に残っていないか grep して警告する
- 途中の API が失敗しても止まらずに続行し、失敗したものを
_failed.txtに記録する (権限不足やサービス未使用でも、取れた分は取る) - 出力は日時付きのディレクトリと
tar.gzにまとめ、既存ファイルを上書きしない
シェルは 2 本に分けています。
| シェル | 取得するもの |
|---|---|
collect-vpc-info.sh |
VPC、サブネット、ルートテーブル、インターネットゲートウェイ (IGW)、NAT ゲートウェイ、VPC エンドポイント、Elastic IP、フローログ、ピアリング、Transit Gateway (TGW)、仮想プライベートゲートウェイ (VGW) / VPN、Amazon EC2、Elastic Load Balancing (ELB)、Amazon RDS、Amazon ElastiCache、AWS Lambda (VPC 内)、Elastic Network Interface (ENI)、セキュリティグループ (SG)、ネットワーク ACL (NACL) |
collect-vpc-extra.sh |
ターゲットグループ、ターゲットのヘルス、ロードバランサー、Amazon Redshift |
使い方
AWS CLI v2 が認証済みの環境で実行します。-n には VPC の Name タグを指定します。
chmod +x collect-vpc-info.sh collect-vpc-extra.sh
# VPC基本情報(必須)
./collect-vpc-info.sh -n <VPCのNameタグ>
# ALB/NLBのターゲットなど(LBがある場合)
./collect-vpc-extra.sh -n <VPCのNameタグ>
# リージョン・プロファイル・出力先も指定できる
./collect-vpc-info.sh -n <VPCのNameタグ> -r ap-northeast-1 -p <プロファイル> -o ./out
実行に必要な権限は、ec2:Describe*、elasticloadbalancing:Describe*、rds:Describe*、elasticache:Describe*、lambda:ListFunctions、redshift:DescribeClusters などです。
足りない権限の項目は _failed.txt に残り、他の取得は続きます。
実行すると、次のように進みます (一部抜粋)。
== 認証情報の確認 ==
== VPC 'cm-draw-io-vpc' を検索中 (region: ap-northeast-1) ==
VPC ID: vpc-xxxxxxxxxxxxxxxxx
== 1. ネットワークの骨格 ==
- vpc OK
- subnets OK
- route-tables OK
...
出力ディレクトリ : ./vpc-info_cm-draw-io-vpc_YYYYMMDD-HHMMSS
アーカイブ : ./vpc-info_cm-draw-io-vpc_YYYYMMDD-HHMMSS.tar.gz
collect-vpc-info.sh
#!/usr/bin/env bash
#
# collect-vpc-info.sh
# VPC構成図の作成に必要な情報を、AWS CLIでまとめて取得する。
# 参照系API(describe / list)のみを使用し、リソースは変更しない。
# 認証情報(PSKなど)は取得しない。
#
# 使い方:
# ./collect-vpc-info.sh -n <VPC名>
# ./collect-vpc-info.sh -n <VPC名> -r us-east-1 -p my-profile -o ./out
#
# 必要なもの: AWS CLI v2(認証済み)。jqは不要。
set -uo pipefail
VPC_NAME=""
REGION="${AWS_REGION:-${AWS_DEFAULT_REGION:-ap-northeast-1}}"
OUT_BASE="."
usage() {
echo "Usage: $0 -n VPC名 [-r リージョン] [-p プロファイル] [-o 出力先ディレクトリ]"
exit 1
}
while getopts "n:r:p:o:h" opt; do
case "$opt" in
n) VPC_NAME="$OPTARG" ;;
r) REGION="$OPTARG" ;;
p) export AWS_PROFILE="$OPTARG" ;;
o) OUT_BASE="$OPTARG" ;;
*) usage ;;
esac
done
[ -z "$VPC_NAME" ] && usage
export AWS_DEFAULT_REGION="$REGION"
export AWS_PAGER=""
if ! command -v aws >/dev/null 2>&1; then
echo "ERROR: aws コマンドが見つかりません。AWS CLI をインストールしてください。" >&2
exit 1
fi
echo "== 認証情報の確認 =="
if ! aws sts get-caller-identity --query "Arn" --output text >/dev/null 2>&1; then
echo "ERROR: AWS認証に失敗しました。プロファイルや認証情報を確認してください。" >&2
exit 1
fi
echo "== VPC '${VPC_NAME}' を検索中 (region: ${REGION}) =="
VPC_IDS=$(aws ec2 describe-vpcs \
--filters "Name=tag:Name,Values=${VPC_NAME}" \
--query "Vpcs[].VpcId" --output text 2>/dev/null)
if [ -z "$VPC_IDS" ] || [ "$VPC_IDS" = "None" ]; then
echo "ERROR: Nameタグが '${VPC_NAME}' のVPCが見つかりません (region: ${REGION})。" >&2
echo " リージョン(-r)やVPC名(-n)を確認してください。" >&2
exit 1
fi
VPC_COUNT=$(echo "$VPC_IDS" | wc -w | tr -d ' ')
VPC_ID=$(echo "$VPC_IDS" | awk '{print $1}')
if [ "$VPC_COUNT" -gt 1 ]; then
echo "WARN: 同名のVPCが ${VPC_COUNT} 件あります (${VPC_IDS})。先頭の ${VPC_ID} を使用します。" >&2
fi
echo "VPC ID: ${VPC_ID}"
TS=$(date +%Y%m%d-%H%M%S)
OUT="${OUT_BASE%/}/vpc-info_${VPC_NAME}_${TS}"
mkdir -p "$OUT"
FAILED="$OUT/_failed.txt"
: > "$FAILED"
# run <ファイル名(拡張子なし)> <コマンド...>
# 失敗しても処理を継続し、失敗内容は _failed.txt に残す。
run() {
local name="$1"; shift
printf ' - %-28s ' "$name"
if "$@" > "$OUT/${name}.json" 2> "$OUT/${name}.err"; then
echo "OK"
[ -s "$OUT/${name}.err" ] || rm -f "$OUT/${name}.err"
else
echo "FAILED (詳細: ${name}.err)"
echo "${name}: $*" >> "$FAILED"
fi
}
echo "== 1. ネットワークの骨格 =="
run vpc aws ec2 describe-vpcs --vpc-ids "$VPC_ID"
run subnets aws ec2 describe-subnets --filters "Name=vpc-id,Values=${VPC_ID}"
run route-tables aws ec2 describe-route-tables --filters "Name=vpc-id,Values=${VPC_ID}"
run igw aws ec2 describe-internet-gateways --filters "Name=attachment.vpc-id,Values=${VPC_ID}"
run natgw aws ec2 describe-nat-gateways --filter "Name=vpc-id,Values=${VPC_ID}"
run endpoints aws ec2 describe-vpc-endpoints --filters "Name=vpc-id,Values=${VPC_ID}"
run eip aws ec2 describe-addresses
run flow-logs aws ec2 describe-flow-logs --filter "Name=resource-id,Values=${VPC_ID}"
echo "== 2. 外部接続 (ピアリング / TGW / VPN) =="
run peering-requester aws ec2 describe-vpc-peering-connections \
--filters "Name=requester-vpc-info.vpc-id,Values=${VPC_ID}"
run peering-accepter aws ec2 describe-vpc-peering-connections \
--filters "Name=accepter-vpc-info.vpc-id,Values=${VPC_ID}"
run tgw-attachments aws ec2 describe-transit-gateway-vpc-attachments \
--filters "Name=vpc-id,Values=${VPC_ID}"
run vgw aws ec2 describe-vpn-gateways \
--filters "Name=attachment.vpc-id,Values=${VPC_ID}"
VGW_IDS=$(aws ec2 describe-vpn-gateways \
--filters "Name=attachment.vpc-id,Values=${VPC_ID}" \
--query "VpnGateways[].VpnGatewayId" --output text 2>/dev/null | tr '\t' ',')
if [ -n "$VGW_IDS" ] && [ "$VGW_IDS" != "None" ]; then
# 認証情報(事前共有キーなど)を含む CustomerGatewayConfiguration と
# Options.TunnelOptions[].PreSharedKey は取得しない(必要な項目だけを指定)。
run vpn-connections aws ec2 describe-vpn-connections \
--filters "Name=vpn-gateway-id,Values=${VGW_IDS}" \
--query "VpnConnections[].{VpnConnectionId:VpnConnectionId,State:State,Type:Type,Category:Category,CustomerGatewayId:CustomerGatewayId,VpnGatewayId:VpnGatewayId,TransitGatewayId:TransitGatewayId,StaticRoutesOnly:Options.StaticRoutesOnly,Tunnels:Options.TunnelOptions[].{OutsideIpAddress:OutsideIpAddress,TunnelInsideCidr:TunnelInsideCidr},Routes:Routes,VgwTelemetry:VgwTelemetry,Tags:Tags}"
else
echo " - vpn-connections SKIP (このVPCにVGWなし)"
fi
echo "== 3. VPC内のリソース =="
run ec2 aws ec2 describe-instances \
--filters "Name=vpc-id,Values=${VPC_ID}" \
--query "Reservations[].Instances[].{Name:Tags[?Key=='Name']|[0].Value,InstanceId:InstanceId,Type:InstanceType,State:State.Name,SubnetId:SubnetId,AZ:Placement.AvailabilityZone,PrivateIp:PrivateIpAddress,PublicIp:PublicIpAddress,SecurityGroups:SecurityGroups[].GroupId}"
run elbv2 aws elbv2 describe-load-balancers \
--query "LoadBalancers[?VpcId=='${VPC_ID}']"
run elb-classic aws elb describe-load-balancers \
--query "LoadBalancerDescriptions[?VPCId=='${VPC_ID}']"
run rds aws rds describe-db-instances \
--query "DBInstances[?DBSubnetGroup.VpcId=='${VPC_ID}'].{Id:DBInstanceIdentifier,Engine:Engine,Class:DBInstanceClass,MultiAZ:MultiAZ,AZ:AvailabilityZone,SecondaryAZ:SecondaryAvailabilityZone,SubnetGroup:DBSubnetGroup.DBSubnetGroupName,Subnets:DBSubnetGroup.Subnets[].SubnetIdentifier,SecurityGroups:VpcSecurityGroups[].VpcSecurityGroupId,PubliclyAccessible:PubliclyAccessible}"
run rds-clusters aws rds describe-db-clusters \
--query "DBClusters[].{Id:DBClusterIdentifier,Engine:Engine,MultiAZ:MultiAZ,SubnetGroup:DBSubnetGroup,Members:DBClusterMembers[].DBInstanceIdentifier}"
run elasticache-subnet-groups aws elasticache describe-cache-subnet-groups \
--query "CacheSubnetGroups[?VpcId=='${VPC_ID}']"
run elasticache-clusters aws elasticache describe-cache-clusters --show-cache-node-info
run lambda-vpc aws lambda list-functions \
--query "Functions[?VpcConfig.VpcId=='${VPC_ID}'].{Name:FunctionName,Subnets:VpcConfig.SubnetIds,SecurityGroups:VpcConfig.SecurityGroupIds}"
# 取りこぼし確認用(EFS、Lambda、エンドポイントなどもENIとして現れる)
run enis aws ec2 describe-network-interfaces \
--filters "Name=vpc-id,Values=${VPC_ID}" \
--query "NetworkInterfaces[].{Id:NetworkInterfaceId,Description:Description,Type:InterfaceType,SubnetId:SubnetId,PrivateIp:PrivateIpAddress,Status:Status,Attachment:Attachment.InstanceId}"
echo "== 4. セキュリティ (SG / NACL) =="
run security-groups aws ec2 describe-security-groups --filters "Name=vpc-id,Values=${VPC_ID}"
run nacls aws ec2 describe-network-acls --filters "Name=vpc-id,Values=${VPC_ID}"
# ---------- 認証情報の混入チェック ----------
if grep -rlE 'PreSharedKey|CustomerGatewayConfiguration|pre_shared_key|SecretAccessKey|"Password"' "$OUT" >/dev/null 2>&1; then
echo "WARN: 出力に認証情報らしき項目が含まれています。共有しないでください。" >&2
grep -rlE 'PreSharedKey|CustomerGatewayConfiguration|pre_shared_key|SecretAccessKey|"Password"' "$OUT" >&2
fi
echo "== サマリー =="
{
echo "VPC名 : ${VPC_NAME}"
echo "VPC ID : ${VPC_ID}"
echo "リージョン : ${REGION}"
echo "取得日時 : $(date '+%Y-%m-%d %H:%M:%S %z')"
echo
echo "[サブネット一覧]"
aws ec2 describe-subnets --filters "Name=vpc-id,Values=${VPC_ID}" \
--query "sort_by(Subnets,&CidrBlock)[].[Tags[?Key=='Name']|[0].Value,SubnetId,CidrBlock,AvailabilityZone,MapPublicIpOnLaunch]" \
--output table 2>/dev/null
} | tee "$OUT/_summary.txt"
ARCHIVE="${OUT}.tar.gz"
tar -czf "$ARCHIVE" -C "$(dirname "$OUT")" "$(basename "$OUT")" 2>/dev/null
echo
echo "出力ディレクトリ : $OUT"
echo "アーカイブ : $ARCHIVE"
if [ -s "$FAILED" ]; then
echo
echo "※ 次の項目は取得に失敗しました(権限不足やサービス未使用の可能性があります):"
cat "$FAILED"
echo " 詳細は各 .err ファイルを確認してください。"
fi
echo
echo "VPN情報は、認証情報を含まない項目だけを取得しています。"
echo "共有前の注意: JSONにはアカウントID、IPアドレス、インスタンスIDなどが含まれます。"
echo " 必要に応じて伏せてから共有してください。"
実行結果として、tar.gz には以下のようなファイルが作成されています。
vpc-info_cm-draw-io-vpc_YYYYMMDD-HHMMSS/
├── _failed.txt # 取得に失敗した項目(今回は空)
├── _summary.txt # VPC名・ID・サブネット一覧のサマリー
├── vpc.json # VPC
├── subnets.json # サブネット
├── route-tables.json # ルートテーブル
├── igw.json # インターネットゲートウェイ
├── natgw.json # NATゲートウェイ
├── endpoints.json # VPCエンドポイント
├── eip.json # Elastic IP(リージョン全体)
├── flow-logs.json # フローログ
├── peering-requester.json # ピアリング(リクエスタ側)
├── peering-accepter.json # ピアリング(アクセプタ側)
├── tgw-attachments.json # Transit Gatewayアタッチメント
├── vgw.json # 仮想プライベートゲートウェイ
├── ec2.json # EC2
├── elbv2.json # ALB/NLB
├── elb-classic.json # Classic Load Balancer
├── rds.json # RDSインスタンス
├── rds-clusters.json # RDSクラスター
├── elasticache-subnet-groups.json # ElastiCacheサブネットグループ
├── elasticache-clusters.json # ElastiCacheクラスター
├── lambda-vpc.json # VPC内のLambda
├── enis.json # ENI(取りこぼし確認用)
├── security-groups.json # セキュリティグループ
└── nacls.json # ネットワークACL
collect-vpc-extra.sh
#!/usr/bin/env bash
#
# collect-vpc-extra.sh
# collect-vpc-info.sh で取得していない「ALB/NLBのターゲット」「Redshift」の情報を追加で取得する。
# 参照系API(describe)のみを使用し、リソースは変更しない。
#
# 使い方:
# ./collect-vpc-extra.sh -n <VPC名>
# ./collect-vpc-extra.sh -n <VPC名> -r us-east-1 -p my-profile -o ./out
#
# 必要な権限: elasticloadbalancing:Describe*, redshift:DescribeClusters, ec2:DescribeVpcs
set -uo pipefail
VPC_NAME=""
REGION="${AWS_REGION:-${AWS_DEFAULT_REGION:-ap-northeast-1}}"
OUT_BASE="."
usage() {
echo "Usage: $0 -n VPC名 [-r リージョン] [-p プロファイル] [-o 出力先ディレクトリ]"
exit 1
}
while getopts "n:r:p:o:h" opt; do
case "$opt" in
n) VPC_NAME="$OPTARG" ;;
r) REGION="$OPTARG" ;;
p) export AWS_PROFILE="$OPTARG" ;;
o) OUT_BASE="$OPTARG" ;;
*) usage ;;
esac
done
[ -z "$VPC_NAME" ] && usage
export AWS_DEFAULT_REGION="$REGION"
export AWS_PAGER=""
if ! command -v aws >/dev/null 2>&1; then
echo "ERROR: aws コマンドが見つかりません。" >&2
exit 1
fi
if ! aws sts get-caller-identity --query "Arn" --output text >/dev/null 2>&1; then
echo "ERROR: AWS認証に失敗しました。プロファイルや認証情報を確認してください。" >&2
exit 1
fi
VPC_ID=$(aws ec2 describe-vpcs \
--filters "Name=tag:Name,Values=${VPC_NAME}" \
--query "Vpcs[0].VpcId" --output text 2>/dev/null)
if [ -z "$VPC_ID" ] || [ "$VPC_ID" = "None" ]; then
echo "ERROR: Nameタグが '${VPC_NAME}' のVPCが見つかりません (region: ${REGION})。" >&2
exit 1
fi
echo "VPC ID: ${VPC_ID} (region: ${REGION})"
TS=$(date +%Y%m%d-%H%M%S)
OUT="${OUT_BASE%/}/vpc-extra_${VPC_NAME}_${TS}"
mkdir -p "$OUT"
FAILED="$OUT/_failed.txt"
: > "$FAILED"
run() {
local name="$1"; shift
printf ' - %-40s ' "$name"
if "$@" > "$OUT/${name}.json" 2> "$OUT/${name}.err"; then
echo "OK"
[ -s "$OUT/${name}.err" ] || rm -f "$OUT/${name}.err"
else
echo "FAILED (詳細: ${name}.err)"
echo "${name}: $*" >> "$FAILED"
fi
}
echo "== ALB/NLB のターゲットグループ =="
# LoadBalancerArns を含むため、ターゲットグループとロードバランサーの対応も分かる
run target-groups aws elbv2 describe-target-groups \
--query "TargetGroups[?VpcId=='${VPC_ID}']"
run load-balancers aws elbv2 describe-load-balancers \
--query "LoadBalancers[?VpcId=='${VPC_ID}'].{Name:LoadBalancerName,Arn:LoadBalancerArn,Type:Type,Scheme:Scheme}"
echo "== ターゲットの登録状況(ターゲットグループごと) =="
# 1行に「名前<TAB>ARN」。ターゲットグループ名はファイル名に使えない文字を置換する
aws elbv2 describe-target-groups \
--query "TargetGroups[?VpcId=='${VPC_ID}'].[TargetGroupName,TargetGroupArn]" \
--output text 2>/dev/null |
while IFS=$'\t' read -r tg_name tg_arn; do
[ -z "${tg_arn:-}" ] && continue
safe=$(printf '%s' "$tg_name" | tr -c 'A-Za-z0-9._-' '_')
run "target-health_${safe}" aws elbv2 describe-target-health --target-group-arn "$tg_arn"
done
echo "== Redshift =="
run redshift aws redshift describe-clusters \
--query "Clusters[?VpcId=='${VPC_ID}'].{Id:ClusterIdentifier,Status:ClusterStatus,NodeType:NodeType,NumberOfNodes:NumberOfNodes,AZ:AvailabilityZone,SubnetGroup:ClusterSubnetGroupName,MultiAZ:MultiAZ}"
ARCHIVE="${OUT}.tar.gz"
tar -czf "$ARCHIVE" -C "$(dirname "$OUT")" "$(basename "$OUT")" 2>/dev/null
echo
echo "出力ディレクトリ : $OUT"
echo "アーカイブ : $ARCHIVE"
if [ -s "$FAILED" ]; then
echo
echo "※ 次の項目は取得に失敗しました(権限不足やサービス未使用の可能性があります):"
cat "$FAILED"
fi
echo
echo "共有前の注意: JSONにはインスタンスID、IPアドレス、ターゲットグループ名などが含まれます。"
echo " 必要に応じて伏せてから共有してください。"
実行すると、tar.gz には以下のようなファイルが作成されます。
vpc-extra_<VPC 名>_<日時>/
├── _failed.txt # 取得に失敗した項目(全部成功なら空)
├── target-groups.json # ターゲットグループ(LoadBalancerArnsも含む)
├── load-balancers.json # ALB/NLB(名前・ARN・種別・Scheme)
├── target-health_app-tg.json # ターゲットグループごとのターゲットのヘルス
├── target-health_api-tg.json # (ターゲットグループの数だけ増える)
└── redshift.json # Redshiftクラスター
補足:Skill の詳細
Skill の構成
Skill 名は aws-drawio-diagram です。「AWS の構成図を作りたい」「draw.io で VPC のネットワーク構成図」などと話しかけると使われるようにしています。
assets/ の drawio ファイルはネットワーク構成ではなく AWS CloudFormation のスタック構成図の例を示したファイルです (本記事での説明には該当しないので説明は省略します)。
aws4-styles.md と validate_drawio.py は Skill 化する際に自動で作成されたファイルです。
aws-drawio-diagram/
├── SKILL.md # 進め方・レイアウト規約・既存VPCの作図手順
├── assets/
│ └── cfn-multi-stack-template.drawio # CloudFormation複数スタック構成の見本 (AWS CloudFormation のスタック構成図を作成する場合)
├── references/
│ └── aws4-styles.md # アイコン・グループ・矢印のスタイル早見表
└── scripts/
└── validate_drawio.py # .drawio の整合性チェック
SKILL.md に書いていること (要約)
以下の内容が SKILL.md の内容となっています。
**1. アイコンは draw.io 組み込みのものを使う**
draw.io には AWS 公式アイコンのセット (`mxgraph.aws4`) が最初から入っています。そのため、アイコンの ZIP をダウンロードして取り込む必要はなく、XML でアイコン名 (`resIcon=mxgraph.aws4.ec2` など) を指定するだけです。最新のアイコンを案内するときは、公式ページ (https://aws.amazon.com/architecture/icons/) を示すようにしています。
**2. 入れ子は親子関係で表現する**
AWS Cloud > リージョン > VPC > AZ > サブネット > EC2 の入れ子は、`container=1` と `parent="<親のID>"` で表します。こうしておくと、draw.io 上でコンテナを動かしたときに中身が一緒に動き、後から手で直しやすくなります。
**3. 矢印の意味を統一する**
| 見た目 | 意味 |
| --- | --- |
| 実線 | 処理の流れ・接続 |
| 点線 | 参照・依存 |
| 赤の破線 (既存 VPC 図) | ルートの向き先が無効 (blackhole) |
**4. 図は横長 (16:9) にする**
スライドに貼ることが多いため、縦長にならないようにしています。ページサイズは 16:9 に近づけ、注記と凡例は図の下ではなく右端のパネルにまとめます。
**5. 検証スクリプトでチェックしてから渡す**
`validate_drawio.py` で、次を確認します。
- エラー: XML が壊れている、ID が重複している、参照先の ID がない、親がない、始点・終点のない矢印
- 警告: 子要素が親の枠からはみ出している、アイコンが重なっている
**6. 既存 VPC から描くときは「事実と推測を分ける」**
Skill の中で、既存 VPC の作図手順を次のように決めています。
- AWS には直接触らず、参照系のシェルをユーザーに実行してもらい、`tar.gz` で返してもらう
- 描く前に**棚卸し**をする。ENI を種別で数え、図に出したリソースと突き合わせて、取りこぼしや数の食い違いを注記する
- サブネットの公開/非公開は、**名前ではなくルートテーブルの `0.0.0.0/0` の向き先**で判定する (IGW ならパブリック)。名前と実態がずれていたら注記する
- 注記に書く数や属性は、記憶や推測ではなく**必ずデータから数える**
- 原因が分からないものは、断定せず「未確認」と書く
**7. 凡例は「実際の矢印」で説明する**
最初は凡例をテキストボックスの文章だけで書いていましたが、図中の線とどれが対応するのか分かりにくいという指摘を受けて変更しました。今は、図中と同じスタイルの**サンプルの矢印やアイコンを左に置き、右に 1 行の短い説明**を添えています。
```text
━━▶ インターネット ↔ IGW
━━▶ IGW ↔ パブリックサブネット
┅┅▶ ルートの向き先が無効(blackhole)
薄いアイコン 削除済みのリソース(取得時点)
```
なお、サンプルの矢印も draw.io 上では「矢印 (エッジ)」なので、始点と終点が必要です。見えない小さな頂点 (`ellipse;fillColor=none;strokeColor=none;` の 2×2) を始点・終点にして、検証スクリプトのエラーを避けています。
まとめ
- 構成ファイルがない既存 VPC でも、参照系のシェル → 棚卸し → draw.io の流れで、AWS 公式アイコンを使った構成図を作れます
- 作図の手順やルール (入れ子、矢印の意味、16:9、検証、棚卸し、凡例) を Skill にしておくと、毎回同じ品質で再現できます
今後は、複数の VPC やアカウントをまたぐ構成図 (TGW、ピアリング) にも広げていきたいと考えています。
クラスメソッドオペレーションズ株式会社について
クラスメソッドグループのオペレーション企業です。
運用・保守開発・サポート・情シス・バックオフィスの専門チームが、IT・AI をフル活用した「しくみ」を通じて、お客様の業務代行から課題解決や高付加価値サービスまでを提供するエキスパート集団です。
当社は様々な職種でメンバーを募集しています。
「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、
クラスメソッドオペレーションズ株式会社 採用サイト をぜひご覧ください。※2026 年 1 月 アノテーション㈱から社名変更しました。









