AnsibleでVPCから作るAWS環境の自動化|ネットワークを先に用意してからEC2を並べる順序設計

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Ansible > AnsibleでVPCから作るAWS環境の自動化|ネットワークを先に用意してからEC2を並べる順序設計
「Ansibleで書いたEC2起動のPlaybookを実行したら、subnet_idが見つからないと怒られた」
「VPCとEC2を別々のPlaybookに分けたが、どちらを先に流せばいいのか毎回迷う」

このエラーが出た時点で、設計に根本的な問題があります。EC2インスタンスはVPCの中に置くリソースです。VPCがなければサブネットも存在せず、EC2に渡すsubnet_idを得ることができません。

この記事では、AnsibleでAWS環境を構築する際の「リソース作成の順序設計」を解説します。VPCから始めてサブネット・IGW・セキュリティグループを用意してからEC2を起動するまでの流れを、Playbookの依存関係とregister変数の使い方を軸に説明します。

動作確認環境: Ansible 9.x / amazon.aws collection 8.x / Python 3.11 / RHEL 9.4

この記事のポイント

・VPC→サブネット→IGW→SG→EC2の順序を守らないとPlaybookは動かない
・register変数でVPC作成結果のIDをEC2タスクへ引き渡す
・role分割でVPCネットワーク層とEC2起動層の依存を明示的に管理する
・subnet_id/vpc_idが見つからないエラーは順序と変数名のミスが原因の9割


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

VPCを先に用意しないとEC2のPlaybookは成立しない

AWSのリソースには親子関係があります。EC2インスタンスはサブネットの中に作るため、サブネットが先に存在していなければEC2タスクは実行できません。サブネットはVPCの中に作るため、VPCが先に存在していなければサブネットタスクは実行できません。

この依存チェーンを図にすると次のようになります。

VPC(アドレス空間の定義) └─ サブネット(AZへの分散配置) └─ ルートテーブル(外への出口の設定) └─ インターネットゲートウェイ(パブリック接続) セキュリティグループ(ポートのホワイトリスト) └─ EC2インスタンス(起動先サブネットとSGを指定して初めて作れる)

Ansibleのタスク順序はこの依存関係を上から下に忠実に追う形で設計します。依存先リソースのIDをregister変数で受け取り、後続タスクへ渡すことが設計の核心です。

前提:amazon.awsコレクションの準備とAWS認証

1. amazon.awsコレクションのインストール

AWSリソースを操作するためのAnsibleモジュールはamazon.awsコレクションに含まれています。標準のansible.builtinには含まれていないため、別途インストールが必要です。

# コレクションのインストール ansible-galaxy collection install amazon.aws # バージョン確認 ansible-galaxy collection list amazon.aws

インストール後の確認出力例(実機):

# /home/ansible/.ansible/collections/ansible_collections Collection Version ------------- ------- amazon.aws 8.2.1

2. AWS認証情報の設定

Playbookの実行前にAWS認証情報を環境変数に設定します。IAMユーザーのアクセスキーを使う場合は以下の形式です。

export AWS_ACCESS_KEY_ID=AKIA...(取得したキーIDを設定) export AWS_SECRET_ACCESS_KEY=wJal...(取得したシークレットを設定) export AWS_REGION=ap-northeast-1

本番環境ではIAMロールをEC2またはIAM Identiy Centerに割り当てて使うことを推奨します。認証情報をPlaybookにハードコーディングするのは絶対に避けてください。

VPCとネットワーク層を先に作るPlaybook設計

1. VPCの作成(network/vpc.yml)

ネットワーク層のPlaybookファイルを作成します。最初にVPCそのものを定義し、CIDRブロックで全体のアドレス空間を確保します。

--- - name: VPCとネットワーク層を構築 hosts: localhost connection: local gather_facts: false vars: region: ap-northeast-1 vpc_cidr: "10.0.0.0/16" public_subnet_cidr: "10.0.1.0/24" private_subnet_cidr: "10.0.2.0/24" az: "ap-northeast-1a" tasks: - name: VPCを作成する amazon.aws.ec2_vpc_net: name: "production-vpc" cidr_block: "{{ vpc_cidr }}" region: "{{ region }}" tags: Environment: production register: vpc_result - name: VPC IDを表示して確認 ansible.builtin.debug: msg: "VPC ID: {{ vpc_result.vpc.id }}"

`register: vpc_result` でVPC作成結果を変数に格納します。後続タスクでは `vpc_result.vpc.id` としてVPC IDを参照します。

2. サブネットの作成(パブリック・プライベートの2層)

VPCのIDを受け取ってサブネットを2つ作成します。パブリックサブネットにはEC2(Webサーバー等)を、プライベートサブネットにはDBやバックエンドを置く設計です。

- name: パブリックサブネットを作成する amazon.aws.ec2_vpc_subnet: vpc_id: "{{ vpc_result.vpc.id }}" cidr: "{{ public_subnet_cidr }}" az: "{{ az }}" map_public: true region: "{{ region }}" tags: Name: "public-subnet-1a" Tier: public register: public_subnet_result - name: プライベートサブネットを作成する amazon.aws.ec2_vpc_subnet: vpc_id: "{{ vpc_result.vpc.id }}" cidr: "{{ private_subnet_cidr }}" az: "{{ az }}" region: "{{ region }}" tags: Name: "private-subnet-1a" Tier: private register: private_subnet_result

`vpc_id` には直前タスクのregister変数から取り出したIDを渡しています。VPCが存在しない状態でこのタスクを実行すると、`vpc_id` が空になってエラーになります。これが「順序が先に来なければならない」理由です。

3. インターネットゲートウェイとルートテーブルの設定

パブリックサブネットからインターネットへ出るためのゲートウェイを作成し、ルートテーブルで0.0.0.0/0宛トラフィックをIGW経由に設定します。

- name: インターネットゲートウェイを作成する amazon.aws.ec2_vpc_igw: vpc_id: "{{ vpc_result.vpc.id }}" region: "{{ region }}" tags: Name: "production-igw" register: igw_result - name: パブリックサブネット用のルートテーブルを作成する amazon.aws.ec2_vpc_route_table: vpc_id: "{{ vpc_result.vpc.id }}" region: "{{ region }}" subnets: - "{{ public_subnet_result.subnet.id }}" routes: - dest: "0.0.0.0/0" gateway_id: "{{ igw_result.gateway_id }}" tags: Name: "public-rtb"

4. セキュリティグループの定義

EC2を起動する前に、どのポートを開けるかをセキュリティグループで定義します。

- name: Webサーバー用のセキュリティグループを作成する amazon.aws.ec2_security_group: name: "web-sg" description: "Web server security group" vpc_id: "{{ vpc_result.vpc.id }}" region: "{{ region }}" rules: - proto: tcp from_port: 80 to_port: 80 cidr_ip: "0.0.0.0/0" - proto: tcp from_port: 443 to_port: 443 cidr_ip: "0.0.0.0/0" - proto: tcp from_port: 22 to_port: 22 cidr_ip: "10.0.0.0/8" tags: Name: "web-sg" register: sg_result

SSHポート22は全開放せず、VPC内のCIDR(10.0.0.0/8)からのみ許可しています。踏み台サーバー経由またはSSM経由でのアクセスを前提にした設計です。

VPCリソースのIDをEC2 Playbookへ渡す設計

1. ネットワーク層の変数をファイルに書き出す

VPC構築PlaybookとEC2起動Playbookを別ファイルに分けると、register変数はPlaybook間で共有されません。IDを引き渡すには一度ファイルに書き出す方法が確実です。

- name: ネットワーク層のIDをホスト変数として保存する ansible.builtin.copy: dest: ./network_ids.yml content: | vpc_id: "{{ vpc_result.vpc.id }}" public_subnet_id: "{{ public_subnet_result.subnet.id }}" private_subnet_id: "{{ private_subnet_result.subnet.id }}" web_sg_id: "{{ sg_result.group_id }}"

このファイルをEC2起動Playbookで `vars_files` として読み込みます。

2. EC2インスタンスの起動タスク(ec2/launch.yml)

--- - name: EC2インスタンスを起動する hosts: localhost connection: local gather_facts: false vars_files: - ../network/network_ids.yml vars: region: ap-northeast-1 # Amazon Linux 2023 AMI(ap-northeast-1) ami_id: ami-0599b6e53ca798bb2 instance_type: t3.small tasks: - name: EC2インスタンスを起動する amazon.aws.ec2_instance: name: "web-server-01" image_id: "{{ ami_id }}" instance_type: "{{ instance_type }}" subnet_id: "{{ public_subnet_id }}" security_groups: - "{{ web_sg_id }}" region: "{{ region }}" network: assign_public_ip: true tags: Role: web Environment: production wait: true register: ec2_result - name: 起動したEC2のパブリックIPを確認する ansible.builtin.debug: msg: "Public IP: {{ ec2_result.instances[0].public_ip_address }}"

`subnet_id` と `security_groups` に、ネットワーク層のPlaybookで書き出したIDを使っています。EC2タスクが実行された時点でサブネットとセキュリティグループは必ず存在するため、依存エラーが起きません。

roleで依存関係をディレクトリ構造として表現する

タスク数が増えると、playbook単体で管理するのが煩雑になります。VPCネットワーク層とEC2起動層をroleに分けると、依存関係がディレクトリ構造として見えるようになります。

roles/ ├── vpc_network/ │ └── tasks/ │ └── main.yml ← VPC・サブネット・IGW・SGを作成 └── ec2_launch/ └── tasks/ └── main.yml ← vpc_networkのregister変数を受け取ってEC2を起動 site.yml ← roleの実行順を明示

site.ymlでroleの順序を明示します。

--- - name: AWS環境を構築する(ネットワーク→EC2の順序) hosts: localhost connection: local gather_facts: false roles: - vpc_network # 必ず先に実行 - ec2_launch # vpc_networkの完了後に実行

Ansibleのrolesは記載順に実行されるため、`vpc_network` が完了してから `ec2_launch` が始まることが保証されます。

トラブルシュート|よくあるエラーと対処法

エラー1: subnet_id が見つからない

fatal: [localhost]: FAILED! => {"msg": "The subnet ID 'subnet-xxxxxxxx' does not exist"}

原因のほとんどは、VPCを作成したリージョンとEC2を起動しようとしているリージョンが食い違っているか、VPC Playbookを先に実行せずにEC2 Playbookだけを実行したケースです。まず `aws ec2 describe-subnets --region ap-northeast-1` でサブネットの実在を確認してください。

エラー2: VPCのCIDRが重複している

An error occurred (InvalidVpc.Conflict) when calling the CreateVpc operation: The CIDR '10.0.0.0/16' conflicts with another vpc

既存VPCとCIDRが重複しています。`aws ec2 describe-vpcs --region ap-northeast-1` で既存VPCのCIDRを確認し、重複しない範囲(10.1.0.0/16など)に変更してください。

エラー3: register変数のキーが変わった

amazon.awsコレクションのバージョンアップで、registerに格納されるキー名が変わることがあります。`ansible.builtin.debug: var: vpc_result` でregister変数の中身を出力して、正しいキーパスを確認してください。

# 変数の中身を確認するデバッグタスク - name: vpc_resultの構造を確認する ansible.builtin.debug: var: vpc_result

本記事のまとめ

作成するリソース 使用するモジュール 依存する先行リソース
VPC amazon.aws.ec2_vpc_net なし(最初に作る)
サブネット amazon.aws.ec2_vpc_subnet VPC ID
インターネットゲートウェイ amazon.aws.ec2_vpc_igw VPC ID
ルートテーブル amazon.aws.ec2_vpc_route_table VPC ID・サブネットID・IGW ID
セキュリティグループ amazon.aws.ec2_security_group VPC ID
EC2インスタンス amazon.aws.ec2_instance サブネットID・セキュリティグループID
AnsibleでAWS環境を構築する際の原則は「依存するリソースを先に作り、そのIDをregister変数で後続タスクへ渡す」です。VPC→サブネット→IGW・ルートテーブル→セキュリティグループ→EC2という順序はAWS側の設計思想そのものであり、Playbookの構造もこの順序を忠実に反映することで、依存エラーを根本から防げます。

ネットワーク層とEC2層をroleに分けてsite.ymlで順序を明示することで、誰がPlaybookを読んでも実行順序が一目でわかる設計になります。チームでAWSを運用する現場ほど、この可読性が後の保守コストを下げます。

AnsibleによるAWS自動化をさらに深く学びたい方は、VPC設計からPlaybook設計・role構成まで体系的に解説した専門講座(Ansible講座 — LinuxMaster.JP)も参考にしてください。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Linux無料マニュアルを受け取る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。