AnsibleでAWSのセキュリティグループを管理する方法|ec2_security_groupモジュールとrole設計でネットワークポリシーを自動化する

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Ansible > AnsibleでAWSのセキュリティグループを管理する方法|ec2_security_groupモジュールとrole設計でネットワークポリシーを自動化する
「AWSのセキュリティグループをコンソールで手作業設定している」「staging と production のルールが少しずつズレていて、どちらが正しいのか追えなくなってきた」。
インフラ担当者からよく聞く悩みですが、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分割すると責務が明確になりレビューコストが下がる


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

なぜセキュリティグループの管理に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

DB 層のルールが Web 層の 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') }}"

セキュリティグループの操作は AWS API への呼び出しなので、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のみ

roleのコードを変えずに環境差分を吸収できるのが、この設計の利点です。「roleはロジック、group_varsは環境設定」という分離を徹底すると、roleの再利用性が高まります。

実践: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

2 回目の実行では、どちらのタスクも 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 の role 設計でコード化し、Git でレビューするフローを最初から作ることが、長期的な運用コストを下げる最も確実な方法です。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Ansibleを使った実際のサーバー構成管理の手順を体系的に学べる無料のLinuxサーバー構築入門マニュアル(図解60P)を配布しています。メルマガ登録で今すぐ受け取れます。
>> Ansible 実践学習の詳細はこちら

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

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

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

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

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

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

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

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

この記事を書いた人

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

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

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