本エントリは「エーピーコミュニケーションズ Advent Calendar 2023」の24日目のエントリです。クリスマスイブ🎄は一切関係ないネタです。
Ansible Automation PlatformのAutomation Controller(旧Ansible Tower / 以下AAP)やAWXはGUIでAnsibleを実行することができるアプリケーションですが、率直に言ってマウス使って操作…とくにプロジェクトやテンプレート作ったりするのって億劫じゃないでしょうか?私は嫌ですやりたくありません(直球)
この記事ではそんなGUI操作が必要なAAPやAWXの操作をAnsibleで自動化するためのタスクの実装について紹介します。
接続方法
サーバーアドレス
最初に戸惑いがちなところですが、抑えるべきポイントは「タスクが実行されるホスト(hosts
指定のホスト)から見たAAPのアドレスを指定する」です。
指定の方法は2通りあり、環境変数CONTROLLER_HOST
で指定するか、タスクごとに各モジュールのcontroller_host
パラメタで指定です。
基本的には1台のAAPに対して様々なリソースを作成していくパターンが多いので、その場合は環境変数をプレイに指定すれば各タスクの記述が減ってplaybookがスッキリします。本記事では環境変数で指定している前提でタスク実装の紹介していきます。
認証情報
これもサーバーアドレスと同様に、環境変数もしくは、タスクごとに各モジュールのcontroller_username
とcontroller_password
で指定します。パスワード認証でなくトークンを使う場合は環境変数CONTROLLER_OAUTH_TOKEN
かパラメタcontroller_oauthtoken
を使います。
通常はサーバーアドレスとセットで指定することになると思います。
運用時の操作であれば適切なロールを設定したユーザーのトークンを使うのが良いですが、AAP/AWXインストールから初期構築までをすべて自動化するのであればadmin
とそのパスワード認証を指定することになります。
(ゼロの状態からトークンでの認証ってできない…よね?)
実装例
ローカル実行でリモートのAAPへ接続する場合
おそらく一番よくあるパターン。
localコネクションプラグインでタスクはローカル実行しつつ、CONTROLLER_HOST
でAAPサーバーを指定する構成。
--- - hosts: localhost environment: CONTROLLER_HOST: 192.168.0.79 CONTROLLER_VERIFY_SSL: false CONTROLLER_USERNAME: admin CONTROLLER_PASSWORD: curry_tabetai tasks: # ...
また、AAPの場合はHTTPS接続でポートも443/TCPなのでアドレスのみ指定すればOKですが、AXWは構成によっては(Kubernetesからの公開方法によっては)HTTPだったりポート番号もデフォルト以外の場合もあります。
その場合は、http
から記述すればOK
(この場合はCONTROLLER_VERIFY_SSL
は無くても動く)
- hosts: localhost environment: CONTROLLER_HOST: http://192.168.0.75:8080 CONTROLLER_VERIFY_SSL: false CONTROLLER_USERNAME: admin CONTROLLER_PASSWORD: ...
タスク実行ノードを中継サーバーにする場合
Ansibleを実行するノードからAAPへ疎通がない場合などの構成。
hosts
で指定する対象ノードから見たAAPのアドレスをCONTROLLER_HOST
に指定。
--- - hosts: server environment: CONTROLLER_HOST: 172.29.1.21 CONTROLLER_VERIFY_SSL: false CONTROLLER_USERNAME: admin CONTROLLER_PASSWORD: curry_tabetai tasks: # ...
AnsibleのマネージドノードとしてAAPのホストに直接接続
aap
に指定しているサーバーにSSH接続し、そこからlocalhost
で接続する、という構成。
複数のAAPサーバーがあり、設定内容が同一であれば、この構成ならまとめて処理できます。
- hosts: aap environment: CONTROLLER_HOST: localhost CONTROLLER_VERIFY_SSL: false CONTROLLER_USERNAME: admin CONTROLLER_PASSWORD: curry_tabetai tasks: # ...
AWXの場合はkubectlコネクションプラグイン使用
SSH接続する構成が取れるのはAAPの場合で、AWXの場合はコンテナ内でSSHは動いていないため、同様の構成をとる場合はkubectl
コネクションプラグインを使う必要があります。
- hosts: awx environment: CONTROLLER_HOST: http://localhost:8052 CONTROLLER_USERNAME: admin CONTROLLER_PASSWORD: ...
※ 以下は2023年12月時点での内容。AWXはたまにコンテナ構成が変わるので、その場合は指定する対象が変化するかも。おそらく「web」に対して接続すればよいと思うけど…
HTTPであることと、接続先のポートが8052なのがポイント(これはServiceリソースのtargetPort
で確認できます)
インベントリファイルは以下のようになります。
この例はAWXはawx
ネームスペースにawx-demo
の名前でデプロイされています。対象はWebのPodでここではawx-demo-web-68b54f6bdf-mx55s
というPod名です。
(いくつかのコンテナで構成されていますがweb
のコンテナに接続します)
[awx] awx ansible_kubectl_pod=awx-demo-web-68b54f6bdf-mx55s ansible_kubectl_namespace=awx ansible_kubectl_container=awx-demo-web [all:vars] ansible_connection=kubectl
kubectl
を実行したときのpodとserviceの状態は以下の通り。
$ kubectl get pod,svc -n awx NAME READY STATUS RESTARTS AGE pod/awx-operator-controller-manager-76b545976d-jlcts 2/2 Running 0 16h pod/awx-demo-postgres-13-0 1/1 Running 0 16h pod/awx-demo-task-5ff94fb4d9-65zdt 4/4 Running 0 16h pod/awx-demo-web-68b54f6bdf-mx55s 3/3 Running 0 15h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/awx-operator-controller-manager-metrics-service ClusterIP 10.43.167.210 <none> 8443/TCP 16h service/awx-demo-postgres-13 ClusterIP None <none> 5432/TCP 16h service/awx-demo-service LoadBalancer 10.43.194.190 192.168.0.75 8080:30448/TCP 16h
kubectl
コネクションプラグインについては以下参照。
パスワードでなくトークンを使用する場合
ここまではCONTROLLER_PASSWORD
を使ってパスワードを指定した例でしたが、トークン(パーソナルアクセストークン)の場合は以下の通り。
environment: CONTROLLER_HOST: 192.168.0.79 CONTROLLER_VERIFY_SSL: false CONTROLLER_USERNAME: zaki CONTROLLER_OAUTH_TOKEN: ....... (ユーザーメニューの「トークン」で作成するトークン文字列)
リソース作成
サブスクリプション
awx.awx.licenseモジュールを使用します。
これはAWXになくAAPのみ。
実行ノード上にzipのマニフェストファイルを配置しておき、パスを指定します。
- name: set the license awx.awx.license: manifest: manifest.zip
マニフェストファイルの作成方法は以下参照。
認証情報
awx.awx.credentialモジュールを使用します。
よく使うものを紹介。
ソースコントロール
Gitリポジトリにアクセスするための認証情報で、主にプロジェクトの更新時に使用。
- name: create git repository credential awx.awx.credential: name: git-repository-credential organization: Default credential_type: Source Control inputs: username: username password: password
公開鍵認証で秘密鍵を登録したい場合は、password
でなくssh_key_data
を使用して、鍵ファイルの内容を指定します。
inputs: username: zaki ssh_key_data: '{{ lookup("file", "~/.ssh/id_rsa") }}'
マシン
Ansible実行時のホストに対するSSH認証情報として使用。
- name: create ssh credential awx.awx.credential: name: server organization: Default credential_type: Machine inputs: username: username password: password
SSH秘密鍵を登録するのであれば前述ソースコントロールの場合と同様にssh_key_data
を使用します。
コンテナレジストリ
認証が必要なコンテナレジストリで使用。
例えばDocker Hubへのログインであればdocker.io
を指定します。
- name: create registry credential awx.awx.credential: name: dockerhub credential organization: Default credential_type: Container Registry inputs: host: docker.io username: username password: password
カスタム認証情報タイプ
認証情報タイプの作成
プリセットの認証情報の型で足りない場合にカスタム認証情報タイプを作成する場合、awx.awx.credential_typeモジュールを使用します。 以下は「ホスト名」「ユーザー名」「パスワード」の3値を一つの情報として定義する例。
インジェクターに{{ 変数名 }}
という文字列を設定するため、raw
を使ってJinja2のテンプレート処理を無効にしているのがポイント。
- name: create credential type for host awx.awx.credential_type: name: host-auth-info kind: cloud inputs: fields: - id: username type: string label: username - id: password type: string label: password secret: true - id: hostname type: string label: hostname injectors: extra_vars: auth_username: "{% raw %}{{ username }}{% endraw %}" # "{{ uaesrname }}" auth_password: "{% raw %}{{ password }}{% endraw %}" # "{{ password }}" auth_hostname: "{% raw %}{{ hostname }}{% endraw %}" # "{{ hostname }}"
ちなみにkind
は必須項目でドキュメントを見るとcloud
かnet
が指定できると書かれているが、どこに作用しているかは不明。。あまり影響しなさそうな気がしますが。。
Note that only cloud and net can be used for creating credential types.
上記の自作認証情報タイプの認証情報
前述host-auth-info
の認証情報タイプを使った認証情報を作成するには、credential_type
に認証情報タイプ名を指定します。
- name: create my credential awx.awx.credential: name: my-credential organization: Default credential_type: host-auth-info inputs: hostname: nijigasaki.example.org username: zaki password: tokimeki
ジョブ関連
インベントリとホスト
awx.awx.inventoryモジュールを使用します。
ホスト作成時にインベントリの指定があるため、まずインベントリを作成してからホストを作成します。
インベントリ作成
- name: create inventory awx.awx.inventory: name: my inventory organization: Default
ホスト作成
awx.awx.hostモジュールを使用します。
inventory
に作成済みインベントリを指定して、そこに属するホストを登録します。
ホスト変数の指定がある場合はvariables
で指定します。
- name: add host to inventory awx.awx.host: name: "{{ item.name }}" inventory: my inventory variables: ansible_host: "{{ item.addr }}" loop: - name: server1 addr: 192.168.0.71 - name: server2 addr: 192.168.0.72
プロジェクト作成
awx.awx.projectモジュールを使用します。
認証が不要なGitリポジトリとしてhttps://github.com/zaki-lknr/ansible-sample.gitを指定するなら以下。
- name: create project awx.awx.project: name: sample project organization: Default scm_type: git scm_url: https://github.com/zaki-lknr/ansible-sample.git scm_update_on_launch: false
認証が必要なリポジトリの場合はcredential
パラメタに作成済みの認証情報名を指定します。
credential: git-repository-credential
実行環境
awx.awx.execution_environmentモジュールを使用してコンテナレジストリ上のEEコンテナを指定します。
認証が必要な場合は、credential
に「credential_type
=Container Registry」を指定して作ったクレデンシャルリソースを指定します。
pull
にはイメージpullポリシーを指定します。
- name: create ee setting awx.awx.execution_environment: name: Network Automation Execution Environment image: docker.io/zakihmkc/ansible-ee:1.2.3 pull: missing credential: dockerhub credential
ジョブテンプレート
ジョブテンプレートを作成するには、awx.awx.job_templateモジュールを使用してここまで掲載したプロジェクトやインベントリ、認証情報、EEなどを指定します。
playbook
にはリポジトリに存在するプレイブックのファイル名を指定する必要があります。(存在しないとエラー)
- name: create job template awx.awx.job_template: name: sample job template job_type: run project: sample project organization: Default inventory: my inventory credentials: - server1 - my-credential playbook: path/to/playbook.yml verbosity: 0 execution_environment: my-image
ワークフローテンプレート…は長いのでまた別途 m__m
awx.awx.workflow_job_template
を使って定義します。
schema
以下に実行したジョブテンプレートを記述して、related
に成功時・失敗時などの次のジョブを指定します。それぞれのジョブテンプレートはidentifier
でタスク定義内で使用するIDを割り振ってそのIDを指定します。
スケジュール
作成済みジョブテンプレートを定期実行させたい場合はawx.awx.scheduleモジュールを使って作成します。
スケジュール定義はrrule
に記述しますが、生で書くのは若干複雑というほどでもないけどハードコーディングせざるを得なくなるため、awx.awx.schedule_rruleset lookupプラグインを使用すると管理しやすいです。
ただしschedule_rruleset
を使用するにはPythonのpytz
パッケージが必要なのでpip
でインストールしておく必要があります。
- name: create schedule awx.awx.schedule: name: sample job template schedule unified_job_template: sample job template rrule: "{{ query('awx.awx.schedule_rruleset', start_time, rules=rrules, timezone='Asia/Tokyo') }}" vars: rrules: - frequency: 'day' interval: 1 # 現在日(リソース作成日)の12時を基準に"%Y-%m-%d HH:MM:SS"書式の文字列作成 start_time: "{{ now(false, '%Y-%m-%d') }} 12:00:00"
あとschedule_rrule
もあって、どちらを使っても以下の警告が出て解決できなかった(運用では実際はこっち使ってたりするけど)
メッセージの感じからplaybookの書きっぷりじゃなくてプラグインの内部実装の方に修正が必要そうな感じがしなくもないけど…詳細不明。
[DEPRECATION WARNING]: The lookup plugin 'awx.awx.schedule_rruleset' was expected to return a list, got '<class 'str'>' instead. The lookup plugin 'awx.awx.schedule_rruleset' needs to be changed to return a list. This will be an error in Ansible 2.18. This feature will be removed in version 2.18. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
…なんかここまで書いてるとlookup plugin使わず生で書いてもよいかもしれない。。
その他
設定
設定についてはawx.awx.settingsモジュールを使用します。
インベントリファイルへの指定でできそうだけどわからなかった「ベースURLの指定」をセットするには以下の通り。
自動化を実行するだけであればこの設定は特にイジらなくても大丈夫だけど、ベースURLは「通知」を行う場合のメッセージ内のURLに使われるので、使い方によってはきちんと設定しておきたい値。
- name: update base url awx.awx.settings: name: TOWER_URL_BASE value: "https://ansible.example.org"
他には、例えばAWX_ISOLATION_SHOW_PATHS
に空リストを設定するには以下。
- name: unset isolation path (workaround) awx.awx.settings: name: AWX_ISOLATION_SHOW_PATHS value: []
ジョブの起動
ジョブを起動するにはawx.awx.job_launchモジュールを使用します。
job_template
にジョブテンプレート名を指定すればOK
- name: launch sample job template awx.awx.job_launch: job_template: sample job template
通知のテスト
モジュールはなさそうなので、uriを使ってAPIを直接叩きます。
URLに含める必要がある通知ID(以下例でのnotify_id
)は事前に知っておく必要があります。
- name: notification test # モジュールが無さそうなのでRESTで実行 ansible.builtin.uri: method: POST url: "https://ansible.example.org/api/v2/notification_templates/{{ notify_id }}/test/" url_username: admin url_password: "{{ password }}" validate_certs: false force_basic_auth: true status_code: [200, 201, 202] # ドキュメントは201となってるが実際は202が返る
ほんとは先に「AAP/AWXの結果をSlackに通知する設定」という記事を書いておきたかったのに…いつか書きます。
環境
- Ansible実行元
- 自動化対象
- AAP Controller 4.4.8 (on RHEL9 / AAP 2.4.3)
- AWX 23.4.0 (on K3s 1.28.4)
弊社では各展示会に出展する際に「ネットワーク自動化デモ」を実施しています。
本記事で紹介したタスクはこのデモ環境を構築するときに使用しており、環境の構築をAnsibleで自動化しています。
(以下懺悔) 1年以上前からこの構築を行っててAnsible使った自動構築の情報としては持っていたので「いつかブログでまとめよう…」と思いつつもずっと保留してる間に、チームメンバーが何人も「AnsibleでAAPリソース作成の自動化」を行う機会があって、でもわかりやすい情報共有ができずに「デモの自動構築のソース参考になるよ」とリポジトリの場所を教えることしかできなかったという機会損失が今年は何度もあり、本当に申し訳ありませんでした。(これが言いたかった)