インフラ担当者からよく聞く悩みですが、Ansible の
ec2_security_group モジュールを使い始めると、この問題は根本から解消されます。この記事では、AWS セキュリティグループを Ansible で管理するための設計思想を解説します。
ec2_security_group モジュールの基本構造から、role として再利用できる変数設計、staging・production の環境差分を group_vars で吸収するパターン、Web 層と DB 層の構成例(実行出力付き)まで踏み込みます。
この記事のポイント
・ec2_security_groupモジュールで冪等なSG管理ができる
・roleのdefaultsに変数を切り出すとstaging/productionの差分管理が楽になる
・group_varsでCIDRや許可ポートを環境別に上書きするのが設計の定石
・Web層とDB層をrole分割すると責務が明確になりレビューコストが下がる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜセキュリティグループの管理にAnsibleが有効なのか
セキュリティグループはAWSの仮想ファイアウォールです。コンソールやAWS CLIで手作業管理していると、時間が経つにつれて次の問題が出てきます。・設定ドリフト:作業メモと実態が乖離し、「なぜこのルールがあるのか」が追えなくなる
・環境間のズレ:stagingに加えたルールがproductionに反映されているか確認しきれない
・変更履歴の散逸:コンソール操作ログにしか残らず、Gitでの差分レビューができない
Ansibleでコード化すると、これらが一気に解決します。YAMLが設計書とコードを兼ね、変更履歴はGitに残ります。何度実行してもルールが勝手に増えず、最後に宣言した状態に収束します(冪等性)。role にまとめれば、staging・production を 1 つのテンプレートで管理できます。
構成管理ツールとしてのAnsibleの設計思想や、SSHを使ったエージェントレスな仕組みについては Ansible入門の解説 も参考にしてください。
ec2_security_groupモジュールの設計思想
ec2_security_group モジュールは amazon.aws コレクションに含まれています。コレクションを導入していない場合は先にインストールが必要です。# コレクションのインストール(requirements.yml を使う場合は後述) ansible-galaxy collection install amazon.aws
1. モジュールの基本パラメーター
モジュールの中心パラメーターは次の4つです。・name:セキュリティグループの名前。Ansibleはこの名前で既存SGを検索し、なければ作成、あれば更新します
・description:SGの説明文(必須。変更後に上書きはできないAWSの仕様に注意)
・vpc_id:対象VPCのID。変数で渡すのが設計上の定石です
・rules:ingressルールのリスト(
rules_egress でegressも指定できます)最もシンプルな宣言例を示します。
- name: Create web-sg security group amazon.aws.ec2_security_group: name: "web-sg" description: "Security group for web servers" vpc_id: "{{ vpc_id }}" region: "{{ aws_region }}" rules: - proto: tcp ports: - 80 - 443 cidr_ip: "0.0.0.0/0" - proto: tcp ports: - 22 cidr_ip: "{{ admin_cidr }}" register: web_sg_result
2. rulesパラメーターの書き方
rules の各要素は「プロトコル・ポート・送信元」の3点セットです。・proto:
tcp・udp・icmp・-1(すべて)のいずれか・ports:ポート番号をリストで記述(単一ポートは整数でも可)
・cidr_ip:IPv4 CIDR。リストで複数指定もできます
・group_id:別SGのIDを指定するとSG参照ルールになります(DB層の設計で後述)
purge_rules: true(デフォルト)を使うと、宣言されていないルールが自動削除されます。「YAMLが唯一の真実(Single Source of Truth)」として使うなら、このデフォルト設定が正解です。逆に、手作業で追加したルールを残したい場合は purge_rules: false に変えますが、そうするとドリフトが発生しやすくなるため注意が必要です。
セキュリティグループをroleに設計する
一つのPlaybookにすべてのSG定義を書き並べると、すぐに管理不能になります。role に分割し、変数で挙動を制御するのが現場の定石です。1. roleのディレクトリ構造
roles/ └── aws_security_group/ ├── defaults/ │ └── main.yml # デフォルト変数(最低優先度) ├── tasks/ │ └── main.yml # 実際のec2_security_group呼び出し └── vars/ └── main.yml # 変更しない固定値(高優先度)
defaults/main.yml に書いた変数は、group_vars や Playbook の vars で上書きできます。「環境ごとに変えたい値はdefaults、変えてはいけない設計値はvars」という分離が基本です。
2. defaults変数の設計
# roles/aws_security_group/defaults/main.yml aws_region: "ap-northeast-1" vpc_id: "" # 必須。呼び出し元で渡す # Web層SG web_sg_name: "web-sg" web_sg_admin_cidr: "10.0.0.0/8" web_sg_allow_public_ports: - 80 - 443 # DB層SG db_sg_name: "db-sg" db_sg_port: 3306
3. tasks/main.ymlの実装
# roles/aws_security_group/tasks/main.yml - name: Create or update web-sg amazon.aws.ec2_security_group: name: "{{ web_sg_name }}" description: "Web servers - managed by Ansible" vpc_id: "{{ vpc_id }}" region: "{{ aws_region }}" rules: - proto: tcp ports: "{{ web_sg_allow_public_ports }}" cidr_ip: "0.0.0.0/0" - proto: tcp ports: - 22 cidr_ip: "{{ web_sg_admin_cidr }}" purge_rules: true register: web_sg - name: Create or update db-sg (allow from web-sg only) amazon.aws.ec2_security_group: name: "{{ db_sg_name }}" description: "DB servers - managed by Ansible" vpc_id: "{{ vpc_id }}" region: "{{ aws_region }}" rules: - proto: tcp ports: - "{{ db_sg_port }}" group_id: "{{ web_sg.group_id }}" purge_rules: true
group_id を参照している点に注目してください。CIDRではなくSG IDを指定することで、「Web層のインスタンス"だけ"がDBに接続できる」という意図がコードで表現されます。Web層のIPが変わっても、SG参照ルールなら修正不要です。
4. Playbookからの呼び出し
# site.yml - name: Manage AWS security groups hosts: localhost connection: local gather_facts: false roles: - role: aws_security_group vars: vpc_id: "{{ lookup('env', 'AWS_VPC_ID') }}"
hosts: localhost・connection: local が正解です。対象インスタンスに SSH 接続する必要はありません。
staging・productionの環境差分をgroup_varsで管理する
インベントリを環境ごとに分けると、group_vars で変数を上書きできます。inventories/ ├── staging/ │ ├── hosts.yml │ └── group_vars/ │ └── all.yml # staging用変数 └── production/ ├── hosts.yml └── group_vars/ └── all.yml # production用変数
# inventories/staging/group_vars/all.yml vpc_id: "vpc-0staging0000000" web_sg_admin_cidr: "0.0.0.0/0" # staging: 自分のPCから直接SSHできるよう全開放 # inventories/production/group_vars/all.yml vpc_id: "vpc-0prod0000000000" web_sg_admin_cidr: "203.0.113.0/28" # production: 管理拠点のCIDRのみ
実践:Web層とDB層の構成を実行した結果
staging インベントリを使って実行した結果を示します。初回実行(リソース新規作成)の出力です。$ ansible-playbook -i inventories/staging site.yml PLAY [Manage AWS security groups] ******************************************** TASK [aws_security_group : Create or update web-sg] ************************** changed: [localhost] => { "changed": true, "description": "Web servers - managed by Ansible", "group_id": "sg-0a1b2c3d4e5f67890", "group_name": "web-sg", "ip_permissions": [ { "from_port": 80, "ip_protocol": "tcp", "ip_ranges": [{"cidr_ip": "0.0.0.0/0"}], "to_port": 80 }, { "from_port": 443, "ip_protocol": "tcp", "ip_ranges": [{"cidr_ip": "0.0.0.0/0"}], "to_port": 443 }, { "from_port": 22, "ip_protocol": "tcp", "ip_ranges": [{"cidr_ip": "0.0.0.0/0"}], "to_port": 22 } ], "vpc_id": "vpc-0staging0000000" } TASK [aws_security_group : Create or update db-sg (allow from web-sg only)] ** changed: [localhost] => { "changed": true, "group_id": "sg-0f9e8d7c6b5a43210", "group_name": "db-sg", "ip_permissions": [ { "from_port": 3306, "ip_protocol": "tcp", "ip_ranges": [], "user_id_group_pairs": [ {"group_id": "sg-0a1b2c3d4e5f67890"} ], "to_port": 3306 } ], "vpc_id": "vpc-0staging0000000" } PLAY RECAP ******************************************************************* localhost : ok=2 changed=2 unreachable=0 failed=0
changed=0 になります。宣言どおりの状態がすでに存在するため、APIへの変更操作が発生しません。これがAnsibleの冪等性です。production で同じ Playbook を実行するときは、インベントリを切り替えるだけです。
# production は admin CIDR が制限されている $ ansible-playbook -i inventories/production site.yml
よくあるエラーと対処
1. botocore.exceptions.NoCredentialsError
AWS 認証情報が解決できていない場合に発生します。~/.aws/credentials のプロファイル設定か、環境変数(AWS_ACCESS_KEY_ID・AWS_SECRET_ACCESS_KEY)を確認してください。EC2インスタンス上で実行するなら、IAMインスタンスプロファイルを使うのが最も安全な設計です。2. InvalidGroup.Duplicate
同名のSGが既存する場合、description が変わっていると AWS がエラーを返します。description はSG作成後に変更できないAWSの仕様です。description は初回から変えない前提で設計してください。3. RulesPerSecurityGroupLimitExceeded
1つのSGに設定できるルール数はデフォルトで60件です。複数サービスのルールを1つのSGに詰め込みすぎているサインです。SG をサービス単位・層単位で分割するタイミングです。4. changed が毎回 true になる
purge_rules: false の状態でルールを追加していると、追加済みのはずのルールが毎回 changed になることがあります。purge_rules: true(デフォルト)に戻し、YAMLに書かれたルールだけを正とする管理に統一してください。
本記事のまとめ
| 設計項目 | 推奨パターン |
|---|---|
| モジュール | amazon.aws.ec2_security_group(FQCNで指定) |
| hosts / connection | localhost / local(SSH不要・AWS API直接呼び出し) |
| 環境差分の吸収 | role の defaults に変数を置き、group_vars で上書き |
| DB層の許可元 | CIDR指定ではなく Web 層の group_id を参照(IP変更に強い) |
| 冪等性の確認 | 2回目実行で changed=0 を確認してからstaging→production展開 |
| purge_rules | true(デフォルト)を維持し、YAML外のルールを残さない |
>> Ansible 実践学習の詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Ansibleのタグ設計入門|実行範囲を制御するタグ付けパターンとnever・alwaysの活用
- この記事の属するカテゴリ:Ansibleへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます