AnsibleでEC2インスタンスを自動作成・管理するPlaybook設計入門|amazon.awsコレクションとec2_instanceモジュールの使い方

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Ansible > AnsibleでEC2インスタンスを自動作成・管理するPlaybook設計入門|amazon.awsコレクションとec2_instanceモジュールの使い方
「AWSのEC2インスタンスを毎回マネジメントコンソールから手作業で作っている」「Terraformを覚えるのは大変だが、手作業での環境構築はもう限界だ」
こういった悩みを持つLinuxエンジニアは少なくありません。

実は、すでにAnsibleを使っているなら、同じPlaybookの構造でEC2インスタンスの作成・起動・停止・削除まで自動化できます。
amazon.awsコレクションのec2_instanceモジュールを使えば、Terraformを導入しなくてもAWSインフラのコード管理が始められるのです。

この記事では、AnsibleのEC2プロビジョニング設計を基礎から解説します。
認証設計・Playbookの基本パターン・プロビジョニング直後の接続設計まで、現場で再現できるレベルで説明します。
動作確認環境: RHEL 9.4 / Amazon Linux 2023(コントロールノード)、Ansible 2.16、amazon.aws 8.x。

この記事のポイント

・amazon.aws.ec2_instanceモジュール1つでEC2を冪等に作成・管理できる
・IAM認証は環境変数またはIAMロールで設定し、Playbookに認証情報を書かない
・group_varsでdev・prod環境を分離するとPlaybookを使いまわせる
・add_hostでプロビジョニング直後から設定Playbookを続けて流せる


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

AnsibleによるEC2プロビジョニングとは|Terraform・AWS CLIとの役割分担

Ansibleはもともとサーバーの設定を自動化するツールですが、amazon.awsコレクションを使えばAWSリソース自体の作成・管理(プロビジョニング)も担えます。

よく比較されるTerraformとの違いはこうです。

Terraform: 状態ファイル(tfstate)でインフラ全体の状態を管理する。複雑なVPC設計・依存関係の解決が得意
Ansible: Playbookの実行ごとに「現在の状態」を確認して冪等に動作する。すでにAnsibleを使っているチームがAWSリソース管理も一元化したい場合に向く
AWS CLI: アドホックな操作に向くが冪等性がなく、同じコマンドを2度実行すると2台のEC2が立つ

「すでにAnsibleでLinuxサーバーを管理しているが、EC2の起動だけAWS CLIで手動でやっている」という現場は多いです。
そこにAnsibleのEC2プロビジョニングを組み込むと、インフラ構築から設定管理まで1本のPlaybookで完結させられます。

amazon.awsコレクションのインストールと認証設計

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

ec2_instanceモジュールを使うにはPythonのAWS SDKであるboto3とbotocoreが必要です。
Ansibleと同じPython環境(通常はvenv内)にインストールしてください。

# boto3・botocoreをインストールする pip install boto3 botocore # amazon.awsコレクションをインストールする(バージョン8.x系) ansible-galaxy collection install amazon.aws # インストール確認 ansible-galaxy collection list | grep amazon.aws

コレクションのインストール先はデフォルトで `~/.ansible/collections/` です。
プロジェクト内で固定したい場合は `collections/requirements.yml` に記載して `ansible-galaxy collection install -r requirements.yml` で管理します。

2. AWS認証情報の設計|IAMロール・環境変数・credentialsファイル

認証情報をPlaybook内にベタ書きするのは厳禁です。以下の3つから現場に合った方法を選んでください。

IAMロール(推奨): Ansibleコントロールノード自体がEC2なら、インスタンスにIAMロールを付けるだけで認証情報の設定が不要になる
環境変数(CI/CDや開発環境向け): `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_DEFAULT_REGION` を設定する
credentialsファイル: `~/.aws/credentials` に複数プロファイルを持つ場合、`AWS_PROFILE` 変数でプロファイルを切り替える

# 環境変数で認証する場合(.envファイルなどで管理する) export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY export AWS_DEFAULT_REGION=ap-northeast-1 # credentialsファイルのプロファイルを切り替える場合 export AWS_PROFILE=myproject-prod

Ansibleに必要なIAMポリシーの最小権限は以下です。

・`ec2:RunInstances`(インスタンス起動)
・`ec2:DescribeInstances`(一覧取得・状態確認)
・`ec2:StopInstances` / `ec2:StartInstances` / `ec2:TerminateInstances`
・`ec2:CreateTags`(Nameタグ付与)

「ec2:*」で全許可するよりも上記の必要権限のみに絞るのが現場のセキュリティ基準です。

ec2_instanceモジュールの基本設計|AMI・インスタンスタイプ・VPCの設定パターン

1. 必須パラメータとstateによる冪等制御

ec2_instanceモジュールの主なパラメータを整理します。
パラメータ 説明 設定例
name インスタンス名(Nameタグ)冪等性のキー web-prod-01
image_id 起動するAMIのID(リージョンごとに異なる) ami-0abc12345678abcde
instance_type インスタンスタイプ t3.micro
key_name SSH接続用キーペア名(AWSに登録済みのもの) my-ec2-keypair
security_groups セキュリティグループ名またはID(リスト) ['web-sg']
vpc_subnet_id 起動先サブネットID subnet-0abc12345678
state 操作の種類(present/stopped/absent/terminated) present
region AWSリージョン ap-northeast-1

`state`の値で操作が変わります。

present: インスタンスが存在しない場合は作成、停止中なら起動する
stopped: 起動中なら停止する。存在しない場合は作成後に停止状態にする
absent / terminated: インスタンスを削除する。存在しない場合は何もしない(冪等)

`name`パラメータがNameタグに使われ、これが冪等性のキーになります。
同じ`name`のインスタンスがすでに起動中なら「changed: false」で何もしません。

2. 最小構成の動作確認Playbook

まず1台のEC2を起動する最小構成のPlaybookで動作を確認します。

# playbooks/provision_ec2.yml --- - name: EC2インスタンスを1台起動する(動作確認用) hosts: localhost connection: local gather_facts: false tasks: - name: Webサーバー用EC2インスタンスを作成・起動する amazon.aws.ec2_instance: name: "web-test-01" image_id: "ami-0a0000000000aaaaa" # Amazon Linux 2023 ap-northeast-1 instance_type: "t3.micro" key_name: "my-ec2-keypair" security_groups: - "default" vpc_subnet_id: "subnet-0abc12345678" state: present region: ap-northeast-1 tags: Environment: test Role: webserver register: ec2_result - name: 起動結果のIPアドレスを表示する ansible.builtin.debug: msg: "インスタンスID: {{ ec2_result.instances[0].instance_id }} プライベートIP: {{ ec2_result.instances[0].private_ip_address }}"

実行結果の例(実機での確認):

$ ansible-playbook playbooks/provision_ec2.yml PLAY [EC2インスタンスを1台起動する(動作確認用)] *** TASK [Webサーバー用EC2インスタンスを作成・起動する] *** changed: [localhost] TASK [起動結果のIPアドレスを表示する] *** ok: [localhost] => { "msg": "インスタンスID: i-0abc12345678abcde プライベートIP: 10.0.1.50" } PLAY RECAP *** localhost : ok=2 changed=1

2回目の実行では`changed=0`になります。同じNameタグのインスタンスがすでに起動中のため、何もしない(冪等)動作を確認できます。

実践Playbook|開発・本番環境のEC2を冪等に管理する設計

1. group_varsで環境別パラメータを分離する

dev・prod環境でインスタンスタイプやサブネットが違う場合は、group_varsに分離するのが定石です。

# ディレクトリ構成 provision/ ├── playbooks/ │ └── provision_ec2.yml ├── group_vars/ │ ├── all.yml # 共通設定(AMI ID、リージョン、キーペア名) │ ├── development.yml # dev環境: t3.micro、devサブネット │ └── production.yml # prod環境: t3.medium、prodサブネット └── inventory/ └── localhost.yml

# group_vars/all.yml aws_region: ap-northeast-1 ec2_ami_id: "ami-0a0000000000aaaaa" # Amazon Linux 2023 ec2_key_name: "my-ec2-keypair" ec2_security_groups: - "web-sg" # group_vars/development.yml env_name: development ec2_instance_type: t3.micro ec2_subnet_id: "subnet-0dev123456789" # group_vars/production.yml env_name: production ec2_instance_type: t3.medium ec2_subnet_id: "subnet-0prod12345678"

2. 環境を引数で切り替えるPlaybook設計

# playbooks/provision_ec2.yml --- - name: EC2インスタンスを環境別にプロビジョニングする hosts: localhost connection: local gather_facts: false # 実行時に -e "env=development" または -e "env=production" で切り替える vars: env: "{{ lookup('env', 'TARGET_ENV') | default('development') }}" tasks: - name: 環境別のgroup_varsを読み込む ansible.builtin.include_vars: file: "group_vars/{{ env }}.yml" - name: Webサーバー用EC2インスタンスを作成する amazon.aws.ec2_instance: name: "web-{{ env_name }}-01" image_id: "{{ ec2_ami_id }}" instance_type: "{{ ec2_instance_type }}" key_name: "{{ ec2_key_name }}" security_groups: "{{ ec2_security_groups }}" vpc_subnet_id: "{{ ec2_subnet_id }}" state: present region: "{{ aws_region }}" tags: Environment: "{{ env_name }}" Role: webserver ManagedBy: ansible register: ec2_result - name: インスタンス情報を変数に保持する ansible.builtin.set_fact: provisioned_private_ip: "{{ ec2_result.instances[0].private_ip_address }}" provisioned_instance_id: "{{ ec2_result.instances[0].instance_id }}" - name: 起動結果を表示する ansible.builtin.debug: msg: - "環境: {{ env_name }}" - "インスタンスID: {{ provisioned_instance_id }}" - "プライベートIP: {{ provisioned_private_ip }}"

実行コマンド:

# 開発環境のEC2を起動する ansible-playbook playbooks/provision_ec2.yml -e "env=development" # 本番環境のEC2を起動する ansible-playbook playbooks/provision_ec2.yml -e "env=production" # 本番環境のEC2を停止する(stateをstopped に変更) ansible-playbook playbooks/provision_ec2.yml -e "env=production" -e "ec2_state=stopped"

AnsibleでAWS構築を体系的に学びたい方へ

Playbookの書き方・Role設計・Vault暗号化・CI/CD連携まで、実機環境で手を動かしながら習得できます。
独学でつまずいている方は、現役エンジニアが指導するハンズオンセミナーで一気に理解を深めましょう。
>> Ansible実践セミナーの詳細・申込はこちら

プロビジョニング後に接続するadd_host設計|EC2作成から設定適用まで1本のPlaybookで完結させる

EC2を作成したら、すぐにそのIPに接続してパッケージインストールや設定ファイル配布を行いたいケースがほとんどです。
`add_host`モジュールを使うと、Playbookの実行中に動的にホストを追加できます。

# EC2プロビジョニング → 初期設定まで1本で流すPlaybook例 --- - name: フェーズ1:EC2インスタンスを作成する hosts: localhost connection: local gather_facts: false tasks: - name: EC2を起動する amazon.aws.ec2_instance: name: "web-prod-01" image_id: "ami-0a0000000000aaaaa" instance_type: t3.medium key_name: my-ec2-keypair security_groups: - web-sg vpc_subnet_id: subnet-0prod12345678 state: present region: ap-northeast-1 tags: Environment: production register: ec2_result - name: EC2が起動完了するまで待つ amazon.aws.ec2_instance_info: instance_ids: "{{ ec2_result.instances[0].instance_id }}" region: ap-northeast-1 register: instance_info until: instance_info.instances[0].state.name == 'running' retries: 10 delay: 15 - name: 新しいEC2をインベントリに追加する ansible.builtin.add_host: name: "{{ ec2_result.instances[0].private_ip_address }}" groups: newly_provisioned ansible_user: ec2-user ansible_ssh_private_key_file: "~/.ssh/my-ec2-keypair.pem" - name: フェーズ2:新しいEC2に初期設定を流す hosts: newly_provisioned gather_facts: true tasks: - name: SSHが応答できる状態になるまで待つ ansible.builtin.wait_for_connection: delay: 10 timeout: 120 - name: パッケージを最新化する ansible.builtin.dnf: name: "*" state: latest become: true - name: Nginxをインストールする ansible.builtin.dnf: name: nginx state: present become: true

`wait_for_connection`を挟むのが重要です。EC2が「running」状態になってもSSHデーモンが起動するまで数秒かかります。
このタスクを省くと「SSH connection refused」でPlaybookがエラー終了することがあります。

よくあるエラーとトラブルシュート

エラーメッセージ 原因 対処
An error occurred (AuthFailure) IAM権限が不足、または認証情報が未設定 IAMポリシーにec2:RunInstances等が含まれているか確認。環境変数が正しくエクスポートされているか確認する
InvalidAMIID.NotFound AMI IDが誤り、またはリージョンが違う aws ec2 describe-images --image-ids ami-XXXX --region ap-northeast-1 で確認する
InvalidSubnetID.NotFound Subnet IDが誤り、または別リージョンのID aws ec2 describe-subnets --region ap-northeast-1 でサブネット一覧を確認する
ModuleNotFoundError: No module named 'boto3' boto3がインストールされていない Ansibleを実行するPython環境にpip install boto3 botocoreを実行する
unreachable: SSH connection refused EC2起動直後でSSHデーモンが未起動 add_hostの前にwait_for_connectionタスクを追加する
You need to install the amazon.aws collection コレクションが未インストール ansible-galaxy collection install amazon.awsを実行する

セキュリティグループでSSH(22番ポート)が開いていない場合も接続できません。
ポートの開放状況はLinux側から確認する場合はssコマンドやlsofによるポート確認の方法も参考にしてください。

【注意】state: absent は本番環境での誤実行に注意

`state: absent` または `state: terminated` を指定したPlaybookを本番環境で誤って実行すると、EC2インスタンスが削除されます。
削除されたインスタンスはストレージも含めて失われ、復元はできません。
本番Playbook実行前には必ず `--check`(ドライランモード)で変更内容を確認する習慣をつけてください。
また、本番環境用のPlaybookには `when: env_name == 'production'` の条件ガードを設けて、誤った環境への適用を防ぐ設計が重要です。

まとめ

この記事では、AnsibleのEC2プロビジョニング設計を解説しました。

やりたいこと Ansible設定・操作
EC2インスタンスを作成・起動する amazon.aws.ec2_instance: state: present
EC2インスタンスを停止する amazon.aws.ec2_instance: state: stopped
EC2インスタンスを削除する amazon.aws.ec2_instance: state: absent
インスタンスIPを取得して後続タスクに渡す register: ec2_resultec2_result.instances[0].private_ip_address
起動後に動的に接続する ansible.builtin.add_host + wait_for_connection
dev・prod環境を1本のPlaybookで管理する group_vars/development.yml / group_vars/production.yml に分離する

次のステップとして、以下のような応用を検討してみてください。

Ansible Vault: AWSのアクセスキーをVaultで暗号化してリポジトリに安全にコミットする
動的Inventory: aws_ec2プラグインを使えば既存EC2を自動で拾えるため、add_hostなしで管理できる
Role化: EC2プロビジョニング部分をroleとして分離すると複数プロジェクトで再利用しやすくなる

関連記事:
Linux ポート確認の全コマンド(ss・lsof・netstat)
Linux DNS 設定の基本(resolv.conf・nmcliの設定方法)

AnsibleでAWS構築を体系的に学びたい方へ

Playbookの書き方・Role設計・Vault暗号化・CI/CD連携まで、実機環境で手を動かしながら習得できます。
独学でつまずいている方は、現役エンジニアが指導するハンズオンセミナーで一気に理解を深めましょう。
>> Ansible実践セミナーの詳細・申込はこちら

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Linux無料マニュアルを受け取る >>

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

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

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

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

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

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

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

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

この記事を書いた人

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

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

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