Ansible
イベント
該当するコンテンツが見つかりませんでした
マガジン
該当するコンテンツが見つかりませんでした
技術ブログ
当記事は、 日常の運用業務(NW機器設定)の自動化 により、 運用コストの削減 および 運用品質の向上 を目標に 「Ansible」 を使用し、様々なNW機器設定を自動化してみようと 試みた記事です。 Ansibleは、OSS版(AWX)+OSS版(Ansible)を使用しております。 Fortigateの「Objects-アドレス」の登録/変更/削除を実施してみた 事前設定 Templateを作成し、インベントリーと認証情報を設定する。 インベントリー:対象機器(ホスト)の接続先/接続方法/使用プラグインを設定する。 ansible_host: xxx.xxx.xxx.xxx --- ansible_host: xxx.xxx.xxx.xxx ← 接続先IPを指定 ansible_connection: httpapi ← FortiGateは、デフォルト接続方式(ssh)ではなく、HTTP/HTTPS(API)を使用 ansible_httpapi_use_ssl: yes ← HTTP/HTTPS(API)を使用する際、HTTPSを使用 ansible_httpapi_validate_certs: no ← HTTP/HTTPS(API)を使用する際、証明書のチェックを無視(証明書が信頼できないエラーを回避) ansible_network_os: fortinet.fortios.fortios ← 接続方式(httpapi)を使用する場合、使用するプラグインを指定 認証情報:対象機器へのログイン情報(ユーザ名/パスワード)を設定。 ユーザ名は 変数:ansible_user に保持される パスワードは 変数:ansible_password に保持される Playbook作成(YAML) 使用モジュール fortinet.fortios.fortios.fortios_firewall_address を使用。 ※参考ページ:https://docs.ansible.com/projects/ansible/latest/collections/fortinet/fortios/fortios_firewall_address_module.html Objects-アドレスの登録(Type:Subnet) アドレス情報(Type:Subnet)を指定して登録(state: ‘present’)を行う。 - name: Add Address(Subnet) fortios_firewall_address: vdom: "root" state: "present" firewall_address: name: "test_add001" type: "ipmask" ← Type:Subnet を指定 subnet: "10.10.10.0 255.255.255.0" ← IP/Netmask を指定 comment: "IP NETMASK" register: wk_result 実行結果:対象のアドレスが登録された。 ※登録後のアドレス情報を抜粋 "msg": { "meta": { "results": [ { "name": "test_add001", "type": "ipmask", "subnet": "10.10.10.0 255.255.255.0", "comment": "IP NETMASK" } ], "status": "success", "vdom": "root" } } Objects-アドレスの登録(Type:IP Range) アドレス情報(Type:IP Range)を指定して登録(state: ‘present’)を行う。 - name: Add Address(IP Range) fortios_firewall_address: vdom: "root" state: "present" firewall_address: name: "test_add002" type: "iprange" ← Type:IP Range を指定 start_ip: "10.10.10.20" ← IP Range(開始) を指定 end_ip: "10.10.10.21" ← IP Range(終了) を指定 comment: "IP RANGE" register: wk_result 実行結果:対象のアドレスが登録された。 ※登録後のアドレス情報を抜粋 "msg": { "meta": { "results": [ { "name": "test_add002", "type": "iprange", "start-ip": "10.10.10.20", "end-ip": "10.10.10.21", "comment": "IP RANGE" } ], "status": "success", "vdom": "root" } } Objects-アドレスの変更 ※同一Typeでの変更パターン アドレス情報(Type:Subnet)を指定して変更(state: ‘present’)を行う。 - name: Add Address(Subnet) fortios_firewall_address: vdom: "root" state: "present" firewall_address: name: "test_add001" type: "ipmask" ← Typeは 変更しない subnet: "10.10.20.0 255.255.255.0" ← IP/Netmaskを "10.10.10.0 255.255.255.0"から変更する comment: "IP NETMASK" register: wk_result 実行結果:対象のアドレスが変更された。※変更後のアドレス情報を抜粋 "msg": { "meta": { "results": [ { "name": "test_add001", "type": "ipmask", "subnet": "10.10.20.0 255.255.255.0", "comment": "IP NETMASK" } ], "status": "success", "vdom": "root" } } Objects-アドレスの変更 ※異なるTypeへの変更パターン アドレス情報(Type:IP Range)を指定して変更(state: ‘present’)を行う。 - name: Change Address(IP Range -> Subnet) fortios_firewall_address: vdom: "root" state: "present" firewall_address: name: "test_add002" type: "ipmask" ← Type:IP Range から変更する subnet: "10.10.30.0 255.255.255.0" ← start_ip: "10.10.10.20",end_ip: "10.10.10.21"から変更する comment: "IP RANGE -> IP NETMASK" register: wk_result 実行結果:対象のアドレスが 正しく変更されない!! ※変更後のアドレス情報を抜粋 "msg": { "meta": { "results": [ { "name": "test_add002", "type": "ipmask", ← Typeは、変更された "subnet": "0.0.0.0 0.0.0.0", ← subnetは、正しく設定されない。。。 "comment": "IP RANGE -> IP NETMASK", } ], "status": "success", "vdom": "root" } } 異なるTypeへの変更対応方法について 現状のAnsibleモジュールでは、異なるTypeへの変更は正しく実施されないことが分かりました。。。 その為、異なるTypeへの変更時は、変更元アドレスを削除した後に 新しいTypeのアドレスを登録するようにしましょう! Objects-アドレスの削除 接続情報とアドレスを指定して削除(state: ‘absent’)を行う。 - name: Delete Address fortios_firewall_address: vdom: "root" state: "absent" firewall_address: name: "test_add001" register: wk_result 実行結果:対象のアドレスが削除された。 ※削除後のアドレス情報なし 最後に 「Ansible」の「 fortinet.fortios.fortios.fortios_firewall_address 」を使用し、「Objects-アドレス」の登録/変更/削除 ができたことは良かった。しかし、現状 設定情報がベタ書きで使い勝手が悪いので、今後 設定内容をINPUTする仕組みを試みたいと思います。これに伴い、何らかの変更申請の仕組みと連携することができ、より設定変更の自動化が活用できるようになると思います。
はじめに こんにちは。バンダイナムネクサス データ戦略部の山野です。 今回は、Google Cloudのサービスを活用してエンジニア向けの開発環境を刷新した事例をご紹介します。私たちの課題と、それをどう解決したかについて、具体的なポイントを深掘りしていきます。 経緯と背景 エンジニア向けの開発環境を、ユーザーと管理者の両方にとってより使いやすく、効率的にしたいという要望がありました。そのため、以下の点に注目して改善を試みました。 マネージドサービスを活用した運用の効率化: 弊チームでは、これまでエンジニア向けの開発環境としてVM環境を提供してきました。しかし、運用コストの増
はじめに こんにちは。プラットフォームエンジニアリングチームに所属している徳富( @yannKazu1 )です。 新規プロダクトを立ち上げるとき、インフラ構築って意外とやることが多いですよね。その中でも地味にめんどくさいのが DBユーザーの作成と権限付与 。手動でやると「あ、権限つけ忘れた」「このユーザー名スペルミスってない?」みたいなヒヤリハットが発生しがちです。 今回は、この作業をTerraformでIaC化した話を書いていきます。 背景:ボイラープレートでインフラ構築を爆速にしている 弊社では Terraformのボイラープレート と、それをもとにインフラを構築するための Devinへの指示プロンプト をセットで管理しているリポジトリがあります。 新規プロダクトのインフラが必要になったら、このリポを使ってDevinにお願いするだけ。数時間もあれば、AWSアカウントの作成、VPC・ECS・Auroraなどの基盤構築、Terraform実行に必要なIAMロール・バックエンド・CI/CDワークフローの設定に加え、Datadog設定、監査設定、ログ基盤の作成まで、必要なインフラがひととおり立ち上がります。 このボイラープレートを整備するにあたって目指したのは、「Devinにお願いするだけで、新規プロダクトのインフラを簡単に作れる状態」でした。ところが、 DBユーザーの作成だけはどうしても手動作業が残ってしまっていました 。 せっかくDevinに投げれば数時間でインフラができるのに、DBユーザーだけは人間が踏み台経由でDBに繋いで CREATE USER して GRANT して……とやらないといけない。これだとボイラープレートの意味が薄れてしまいますし、手動オペレーションにはミスのリスクもあります。 ここをTerraformでIaC化できれば、ボイラープレートにサクッと組み込めて、Devinに任せるだけでインフラ構築が完結するようになります。 なぜTerraform? DBユーザーの管理といえばAnsibleを使うパターンも考えました。ただ、弊社のインフラは基本的にTerraformで一元管理しているので、できればTerraformの中で完結させたい。ツールが増えると学習コストも運用コストも増えますし、ボイラープレートにAnsibleのステップを追加するよりも、Terraformモジュールとして組み込む方がシンプルです。 というわけで、Terraformでなんとかする方向で調査を進めました。 Aurora Data APIという選択肢 弊社ではAurora MySQLを使用しており、バージョン3.07以降でRDS Data APIに対応しています。 Data APIは、HTTPSエンドポイント経由でSQLを実行できるAPIです。従来のようにVPC内から直接DBに接続する必要がなく、AWS CLIやSDKからサクッとSQLを叩けます。 これを terraform_data リソースの local-exec プロビジョナーと組み合わせれば、Terraformの中からDBユーザーを作成できるというわけです。 モジュールの実装 実際に作ったTerraformモジュールを紹介します。 DBユーザーの作成・削除 resource "terraform_data" "db_user" { input = { rds_cluster_arn = var.rds_cluster_arn rds_secret_arn = var.rds_secret_arn database_name = var.database_name username = var.username ssm_parameter_name = var.ssm_parameter_name } provisioner "local-exec" { command = <<-EOT PASSWORD=$(aws ssm get-parameter --name "$ { self.input.ssm_parameter_name } " --with-decryption --query 'Parameter.Value' --output text) aws rds-data execute-statement \ --resource-arn "$ { self.input.rds_cluster_arn } " \ --secret-arn "$ { self.input.rds_secret_arn } " \ --database "$ { self.input.database_name } " \ --sql "CREATE USER IF NOT EXISTS '$ { self.input.username } '@'%' IDENTIFIED BY '$PASSWORD'" EOT } provisioner "local-exec" { when = destroy command = <<-EOT aws rds-data execute-statement \ --resource-arn "$ { self.input.rds_cluster_arn } " \ --secret-arn "$ { self.input.rds_secret_arn } " \ --database "$ { self.input.database_name } " \ --sql "DROP USER IF EXISTS '$ { self.input.username } '@'%'" EOT } } ポイントは以下のとおりです。 terraform_data リソースを使って、 local-exec プロビジョナーでData API経由のSQLを実行 CREATE USER IF NOT EXISTS で冪等性を担保 when = destroy のプロビジョナーで、 terraform destroy 時にユーザーを自動削除 パスワードはSSM Parameter Storeから取得(後述の工夫で安全に管理) 権限の付与・取り消し resource "terraform_data" "db_grant" { for_each = { for idx, grant in var.grants : idx => grant } depends_on = [ terraform_data.db_user ] input = { rds_cluster_arn = var.rds_cluster_arn rds_secret_arn = var.rds_secret_arn database_name = var.database_name username = var.username privileges = each.value.privileges grant_database = coalesce (each.value.database, var.database_name) grant_table = coalesce (each.value.table, "*" ) } provisioner "local-exec" { command = <<-EOT aws rds-data execute-statement \ --resource-arn "$ { self.input.rds_cluster_arn } " \ --secret-arn "$ { self.input.rds_secret_arn } " \ --database "$ { self.input.database_name } " \ --sql "GRANT $ { self.input.privileges } ON $ { self.input.grant_database } .$ { self.input.grant_table } TO '$ { self.input.username } '@'%'" EOT } provisioner "local-exec" { when = destroy command = <<-EOT aws rds-data execute-statement \ --resource-arn "$ { self.input.rds_cluster_arn } " \ --secret-arn "$ { self.input.rds_secret_arn } " \ --database "$ { self.input.database_name } " \ --sql "REVOKE $ { self.input.privileges } ON $ { self.input.grant_database } .$ { self.input.grant_table } FROM '$ { self.input.username } '@'%'" || true EOT } } for_each で権限セットを柔軟に定義できるようにしています。DMLとDDLを分けて付与したい、みたいなケースにも対応可能です。 使い方 モジュールの呼び出しはこんな感じです。 module "db_user_app" { source = "../../modules/db_user" rds_cluster_arn = module.aurora_main.cluster_arn rds_secret_arn = module.aurora_main.cluster_master_user_secret [ 0 ] .secret_arn database_name = "hoge_db" username = "hoge_app_user" ssm_parameter_name = aws_ssm_parameter.app_db_password.name depends_on = [ aws_ssm_parameter.app_db_password, module.aurora_main, ] grants = [ { # DML権限: データの読み書き privileges = "SELECT, INSERT, UPDATE, DELETE" } , { # DDL権限: マイグレーション実行用(テーブル作成・変更・削除、インデックス操作) privileges = "CREATE, ALTER, DROP, INDEX, REFERENCES" } ] } DMLとDDLの権限を分けて書けるのが個人的に気に入っています。どの権限を付与しているかが一目でわかりますし、将来的に読み取り専用ユーザーを追加したいときもモジュールを再利用するだけでOKです。 パスワードをstateに残さない工夫 ここが今回一番こだわったポイントです。 Terraformでパスワードを扱うとき、普通にやるとstateファイルにパスワードが平文で残ってしまいます。 sensitive = true をつけても、plan出力でマスクされるだけでstateには書き込まれてしまう。これはセキュリティ的によろしくないですよね。 そこで活用したのが、 Terraform 1.10で導入された ephemeral リソース と、 Terraform 1.11で導入された value_wo (write-only引数) です。 ephemeral "random_password" "app_db" { length = 32 special = false } resource "aws_ssm_parameter" "app_db_password" { name = "/$ { var.app_name } /$ { var.env } /db/app_user_password" type = "SecureString" value_wo = ephemeral.random_password.app_db.result value_wo_version = 1 } ephemeral リソースとは Terraform 1.10で登場した新しいリソースタイプです。通常の resource や data と違い、planやstateに一切値が保存されません。毎回のplan/apply時に一時的に生成され、使い終わったら破棄されます。 ephemeral "random_password" を使うことで、パスワードの生成結果がstateに残ることを防げます。従来の resource "random_password" だと、生成したパスワードがstateファイルにそのまま記録されてしまっていたので、これは大きな改善です。 value_wo (write-only引数)とは Terraform 1.11で導入されたwrite-only引数の仕組みです。 aws_ssm_parameter リソースの value_wo を使うと、SSM Parameter Storeへの書き込みは行われますが、その値がTerraformのstateやplanファイルに保存されることはありません。 value_wo_version は変更検知のためのバージョン番号です。パスワードをローテーションしたい場合はこの値をインクリメントすれば、次のapply時に新しいパスワードが生成・設定されます。逆にバージョンを変えなければ、 ephemeral が毎回新しいパスワードを生成しても、SSM Parameter Storeの値は更新されません。 この2つを組み合わせることで、パスワードの生成から保存まで、 一度もstateファイルにパスワードが記録されない フローが実現できました。 現時点での制約:パスワードの更新には対応していない ここまで読んで「パスワードのローテーションはどうするの?」と思った方もいるかもしれません。正直に言うと、 このモジュールはパスワードの更新( ALTER USER )には対応していません 。 理由はシンプルで、Terraformのプロビジョナーには when = create と when = destroy しかなく、 when = update が存在しない からです。つまり、リソースの入力値が変わったときに「更新用のSQLを実行する」ということができません。 terraform_data の input が変わると、Terraformは古いリソースをdestroyしてから新しいリソースをcreateする(replace)動きになります。DBユーザーの場合、これは DROP USER → CREATE USER という流れになるので、パスワード変更だけしたいケースには少々大げさです。 この when = update の追加については、HashiCorpのGitHubリポジトリにIssueが上がっています( hashicorp/terraform#35825 )。いつか実装されれば、パスワードローテーションもこのモジュールの中でスマートに対応できるようになるはずです。それまでは、パスワードの更新が必要になった場合は手動対応か、別の仕組みで対応する必要があります。 とはいえ、新規プロダクト立ち上げ時の初期ユーザー作成という用途においては、create/destroyだけで十分に機能しています。 まとめ やったことをまとめると以下のとおりです。 Aurora MySQLのData API(3.07以降で利用可能)を使って、Terraformの terraform_data + local-exec でDBユーザーの作成・権限付与をIaC化 Terraform 1.10の ephemeral リソースと1.11の value_wo を活用して、パスワードをstateに残さないセキュアな構成を実現 これらをモジュール化してボイラープレートに組み込み、新規プロダクトのインフラ構築をさらにスムーズに プロビジョナーに when = update がないため、パスワードの更新には現時点では未対応 Terraformの ephemeral や value_wo はまだ比較的新しい機能なので、知らない方も多いかもしれません。パスワード管理で困っている方はぜひ試してみてください。 参考リンク Amazon Aurora MySQL で RDS Data API のサポートを開始 RDS Data API でサポートされているリージョンと Aurora DB エンジン - Amazon Aurora Terraform 1.11 brings ephemeral values to managed resources with write-only arguments Ephemeral values in Terraform Ephemeral values in resources | Terraform | HashiCorp Developer Use temporary write-only arguments | Terraform | HashiCorp Developer I would like provisioner/s to support when=update - hashicorp/terraform#35825
動画
該当するコンテンツが見つかりませんでした









