2022年8月20日土曜日

AnsibleでWindows Serverの初期設定と単体テストを行う

今回は、Ansibleを使ってWindows Serverの初期設定と単体テストを行うPlaybookを作成したので紹介する。作成する中で、Playbookのテクニックについても知見を得られたので、それも併せて記載する。

Windows Serverの初期設定とは、例えば役割と機能の追加や必要なサービスの起動・不要なサービスの停止作業が該当する。また、Windows Serverの場合はActive Directoryのドメインに参加をすることが多いので、その作業もAnsibleにて自動化してみた。

今までのAnsible関連記事

環境

コントロールノードのLinuxのOSは、AlmaLinuxにて構築している。実行対象のWindows Serverは、Windows Server 2019とした。

  • コントロールノード
    • OS : AlmaLinux 8.5
    • Ansible : ansible [core 2.13.1]
  • Ansible実行対象サーバ
    • OS : Windows Server 2019

Playbookの処理概要

今回のPlaybookはWindows Serverの初期構築を行うものとなるが、大きく「構築パート」と「単体テストパート」に分けて作成している。

構築パートでAnsibleのモジュールを用いてWindows Serverの各種設定や役割と機能の追加などを行う。

単体テストパートでは、構築パートで実施した設定項目と一対一となるように設定確認を行う。しかし本来、Ansibleは冪等性 (処理を何回行っても必ず同じ結果となる性質) があるため、構築パートでAnsibleの処理が成功したことを確認できれば必ず正しい設定が反映されているはずであり、わざわざ単体テストは不要と感じるかもしれない。

ただし、再度設定値確認として単体テストを行うことで、Ansibleでは処理が成功していても何らかの理由で設定が反映されていない可能性を排除できることや、Ansibleの構築処理とは別に設定変更をさせずに単体テストを行うことができるため品質向上を図ることができると考える。

また、基本方針として、単体テストパートの処理を構築パートで利用したモジュールや処理と同じにしてしまうと、万が一設定変更処理自体に問題があった場合に検知ができない可能性があることから、単体テストパートでは別の処理内容 (別のコマンド) にて設定内容を確認をするようにPlaybookを作成した。

Playbookの処理詳細

Playbookのファイルの記載内容は大きく以下のように構成される。

設定項目 説明
hosts 実行対象のサーバを記載する。サーバのリストは別ファイルで作成し、そのファイルの中で実行する対象のサーバやグループを指定する。
vars 変数を記載する。ここで記載した変数は{{ 変数名 }}という形で参照できる。また、変数は配列して複数の要素を持たせることもできる。
tasks Ansibleで実行するメインの処理を記載する。
handlers Ansibleでメイン処理実行状況に応じて実行する事後処理を記載する。例えば、設定ファイル更新がされた場合 (実行結果がchangedの場合) は設定反映のためにサービス再起動を行うといった使い方をする。今回のPlaybookではHotfixを保存したNASマウント成功時のみアンマウントを行うよう、NASアンマウントのタスクを定義する。

図(saikei)

Ansibleのベストプラクティスでは、これらのファイルを分割して決められたディレクトリに沿って配置するルールとなっている。今回のPlaybookも以下の通り、ベストプラクティスの配置で作成した。

.
├── staging                                     # インベントリファイル (テスト環境)
├── site.yml                                    # Playbookを呼び出すファイルとして利用
├── setup_widnows_server.yml                    # Playbook本体 (実行対象のロールを定義)
├── group_vars
│   ├── all.yml                                 # 全体共通変数
│   └── windows_servers.yml                     # Windows Server用変数
└── roles
    └── setup_windows_server                    # ロール (Windows Server初期設定)
        ├── tasks
        │   ├── main.yml                        # タスクを呼び出すファイルとして利用
        │   ├── build.yml                       # タスク本体 (Windows Server初期設定)
        │   └── unit_test.yml                   # タスク本体 (Windows Server単体テスト)
        └── handlers
             └── main.yml                        # ハンドラーのタスク本体

それでは、実際のファイルの内容を具体的に見ていこう。

site.yml : Playbookを呼び出すファイルとして利用

site.ymlでは、今後の拡張性を考慮してimport_playbookを用いてPlaybook本体を呼び出す構成とする。当然だが、直接呼出し先のPlaybookの内容を記載しても動作する。

---
- import_playbook: setup_windows_server.yml

setup_windows_server.yml : Playbook本体 (実行対象のロールを定義)

Playbook本体では、実行対象のロールを定義する。今回実行するロールは1つとなる。

  • setup_windows_server : Windows Serverの初期設定と単体テストを行うロール

実行するロールに対して、hostsgather_factsといった内容を定義する。なお、タグ情報をtagsで付与しているが、これはPlaybook実行時に特定のロールだけ実行したい場合に利用する。

---
- name: Setup windows server
  hosts: windows_servers
  gather_facts: true

  roles:
    - role: setup_windows_server
      tags: setup_windows_server

group_vars/all.yml : 全体共通変数

実行対象サーバ全台で共通して利用する変数を定義する。今回は、Windows Serverに接続するためのユーザや接続方式にWinRMを利用することなどを変数として定義した。

変数名 内容
ansible_user Ansible実行用ユーザを指定。
ansible_password Ansible実行用ユーザのパスワードを指定。パスワードはAnsible Vaultにて暗号化されたものを記載している。
ansible_port Ansibleが接続する際に使用するポート番号を指定。WinRMを使用するため、5986を使用する。
ansible_connection Ansibleが接続に使用するプロトコルを指定する。WinRMを指定する。
ansible_winrm_server_cert_validation WinRM接続時にサーバの証明書検証を無効化する。
---
ansible_user: ansibleuser
ansible_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  33663331343562653639326336376337643964303935613838333166363362343063313038303164
  ~(中略)~
  64313233396132366361336434376264376361666235633534353631383938616134
ansible_port: 5986
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore

group_vars/windows_servers.yml : Windows Server用変数

Windows Server用の変数を定義する。

変数名 内容
win_local_user 作成するローカルユーザ名とパスワードを設定する。今回はlocaladminとansibleuserという2つのユーザを作成する。
win_hostfix_list インストールするHotfixを指定する。
win_feature_install_list インストールする役割と機能を指定する。例として今回はWindows Serverバックアップのインストールを行う。
win_service_disable_list 無効化するサービスを指定する。
win_service_enable_list 有効化するサービスを指定する。
win_domain 参加するドメイン名と、ドメイン参加時に使用するドメインユーザ、パスワードを指定する。
win_eventlog イベントログの保存容量とイベントログが最大値に達した際の動作を指定する。
---
win_local_user:
  - name: localadmin
    password: !vault |
      $ANSIBLE_VAULT;1.1;AES256
      64343535356239656637336163333664313530636237316138326531346130383832663664316238
      ~(中略)~
      3133323736313336610a363262343231366163653561356637386130326364383436396538383935
      3732
  - name: ansibleuser
    password: !vault |
      $ANSIBLE_VAULT;1.1;AES256
      33663331343562653639326336376337643964303935613838333166363362343063313038303164
      ~(中略)~
      64313233396132366361336434376264376361666235633534353631383938616134

win_hostfix_list:
  - name: kb5005112
    path: C:\temp\windows10.0-kb5005112-x64_81d09dc6978520e1a6d44b3b15567667f83eba2c.msu

win_feature_install_list:
  - Windows-Server-Backup

win_service_disable_list:
  - AxInstSV
  - ALG
  - bthserv

win_service_enable_list:
  - W32Time

win_domain:
  name: test.local
  user: join_domain@test.local
  password: !vault |
      $ANSIBLE_VAULT;1.1;AES256
      30343031393862363564306338633233613961396136653161333661353365636336386263353038
      ~(中略)~
      64393264613537313230626364343564663539643166653739383833623164663365

win_eventlog:
  max_size: 20480KB
  retention: OverwriteAsNeeded

roles/setup_windows_server/tasks/main.yml : タスクを呼び出すファイルとして利用

各ロール内のmain.ymlでは、今後の拡張性を考慮してinclude_tasksを用いて別のファイルに記載したタスクを呼び出す構成とする。当然だが、直接呼出し先のタスクの内容を記載しても動作する。

ここでもタグ情報をtagsで付与しているが、これはPlaybook実行時に特定のタスクだけ実行したい場合に利用する。

---
# Build
- block:
  - name: Include build tasks
    include_tasks: file=build.yml
  tags: windows_build

# Unit test
- block:
  - name: Include unit test tasks
    include_tasks: file=unit_test.yml
  tags: windows_unit_test

roles/setup_windows_server/tasks/build.yml : タスク本体 (Windows Server初期設定)

Windows Serverの初期設定では以下タスクを実行する。大きくはcommunity.windowsansible.windowsの2種類のモジュールを用いる。それぞれのモジュールの詳細は、以下を参照すること。

タスク名 設定
Set timezone community.windows.win_timezoneモジュールを用いてタイムゾーンを設定する。
Create local users ansible.windows.win_userモジュールを用いて、ローカルユーザの作成を行い初期パスワードを設定する。ユーザ作成済みであってもパスワードが異なる場合は、パスワードを設定しなおしてくれる。グループはAdministratorsグループに所属させ、パスワードの有効期限は無期限とする。また、Ansible実行ログにパスワード情報が記録されることを防ぐため、no_log: trueを設定する。
Mount nfs volume Hotfixを保存したNASをマウントする。アンマウントに成功した場合は、Playbook実行後、ハンドラーにてアンマウントを行うため、notifyにてタスク名を指定する。AnsibleコントロールノードにてNASを一度だけマウントするよう、run_once: trueを設定する。
Copy hotfix files Hotfixをコピーする。ファイルコピーにはansible.windows.win_copyモジュールを用いる。
Install hotfix community.windows.win_hotfixモジュールを用いてコピーしたHotfixを適用する。
Set hostname ansible.windows.win_hostnameモジュールを用いてホスト名を設定する。変更が発生した場合は再起動を行う。
Install windows feature ansible.windows.win_featureモジュールを用いてWindowsの役割と機能の追加を行う。
Disable service ansible.windows.win_serviceを用いてサービスを無効化する。
Enable service ansible.windows.win_serviceを用いてサービスを有効化する。
Disable ipv6 of network adapter community.windows.win_net_adapter_featureモジュールを用いてネットワークアダプタのIPv6の設定を無効化する。
Join domain ansible.windows.win_domain_membership用いてActive Directoryドメインに参加する。参加後にOS再起動を行う。
Set eventlog settings community.windows.win_eventlog用いて、イベントログのログ容量とイベントログが最大値に達した際の動作を設定する。対象のイベントログは、システム、アプリケーション、セキュリティとする。
---
# タイムゾーン設定
- name: Set timezone
  community.windows.win_timezone:
    timezone: Tokyo Standard Time

# ローカルユーザ設定
# パスワード情報表示を抑制するためno_logを付与
- name: Create local users
  ansible.windows.win_user:
    name: "{{ item.name }}"
    fullname: ""
    password: "{{ item.password }}"
    password_never_expires: true
    groups:
      - Administrators
    state: present
  loop: "{{ win_local_user }}"
  no_log: true

# Hotfix適用 (再起動は後続処理で併せて実施)
- name: Mount nfs volume
  ansible.posix.mount:
    src: "192.168.1.1:/public/Windows Server 2019/2022-07_hotfix"
    path: /ansible_mnt
    opts: ro
    boot: false
    state: mounted
    fstype: nfs
  delegate_to: localhost
  run_once: true
  notify: Unmount nfs volume

- name: Copy hotfix files
  ansible.windows.win_copy:
    src: /ansible_mnt/
    dest: C:\Temp

- name: Install hotfix
  community.windows.win_hotfix:
    hotfix_kb: "{{ item.name }}"
    source: "{{ item.path }}"
    state: present
  register: result
  loop: "{{ win_hostfix_list }}"

# ホスト名設定&再起動
- name: Set hostname
  ansible.windows.win_hostname:
    name: "{{ inventory_hostname }}"
  register: result

- name: Reboot after changed hostname
  ansible.windows.win_reboot:
  changed_when: false
  when: result.reboot_required

- name: Wait for reboot
  ansible.builtin.wait_for_connection:
    delay: 5
    timeout: 120
  when: result.reboot_required

# 役割と機能の追加
- name: Install windows feature
  ansible.windows.win_feature:
    name: "{{ win_feature_install_list }}"
    state: present

# サービス無効化
- name: Disable service
  ansible.windows.win_service:
    name: "{{ item }}"
    start_mode: disabled
  loop: "{{ win_service_disable_list }}"

# サービス有効化
- name: Enable service
  ansible.windows.win_service:
    name: "{{ item }}"
    start_mode: auto
  loop: "{{ win_service_enable_list }}"

# インタフェースのIPv6無効化
- name: Disable ipv6 of network adapter
  community.windows.win_net_adapter_feature:
    interface: Ethernet0
    component_id: ms_tcpip6
    state: disabled

# ドメイン参加&OS再起動
- name: Join domain
  ansible.windows.win_domain_membership:
    dns_domain_name: "{{ win_domain.name }}"
    domain_admin_user: "{{ win_domain.user }}"
    domain_admin_password: "{{ win_domain.password }}"
    state: domain
  register: result

- name: Reboot after joined domain
  ansible.windows.win_reboot:
  changed_when: false
  when: result.reboot_required

- name: Wait for reboot
  ansible.builtin.wait_for_connection:
    delay: 5
    timeout: 120
  when: result.reboot_required

# イベントログ設定
# maximum_sizeは64KBの倍数で設定
- name: Set eventlog settings
  community.windows.win_eventlog:
    name: "{{ item }}"
    maximum_size: "{{ win_eventlog.max_size }}"
    overflow_action: "{{ win_eventlog.retention }}"
    state: present
  loop:
    - System
    - Application
    - Security

roles/setup_windows_server/tasks/unit_test.yml : タスク本体 (Windows Server単体テスト)

Windows Serverの単体テストでは以下タスクを実行する。ansible.windows.win_shellモジュールを用いてPowerShellによる設定値確認を行うよう構成した。

タスク名 設定
Test timezone タイムゾーンを確認する。
Test local users ユーザが存在することを確認する。
Test hotfix Hotfixの適用状態を確認する。
Test hostname ホスト名を確認する。
Test install windows features Windowsの役割と機能のインストール状態を確認する。
Test disable service サービスを無効化状態を確認する。
Test enable service サービスを有効化状態を確認する。
Test disable ipv6 ネットワークアダプタのIPv6の設定を無効化されていることを確認する。
Test ntp sync NTPサーバとの時刻同期の状態を確認する。w32tmコマンドでは正確に同期をできていることの表示がされないことから、最終正常同期時刻がテスト日時と同一(「時」まで一致)であることを確認することで、時刻同期の正常性を確認する。
Test domain 所属しているドメイン名を確認する。
Test domain (Check DNS lookup) 自分自身のホスト名がDNSにて名前解決できることを確認する。
Test eventlog settings (MaxSize) イベントログのログ容量を確認する。
Test eventlog settings (Retention) イベントログが最大値に達した際の動作を確認する。レジストリ値を確認し、イベントを上書きする:0、上書きしない:4294967295と値と設定されることを判定する。
---
# タイムゾーン確認
- name: Test timezone
  ansible.windows.win_shell: |
    Get-TimeZone | Where-Object { $_.Id -eq "Tokyo Standard Time" }
  changed_when: false
  register: result
  failed_when: result.stdout == ""

# ローカルユーザ確認
- name: Test local users
  ansible.windows.win_shell: |
    Get-LocalUser | Select-Object Name, Enabled, Description | Where-Object { $_.Name -eq "{{ item.name }}" }
  changed_when: false
  register: result
  failed_when: result.stdout == ""
  loop: "{{ win_local_user }}"
  no_log: true

# Hotfix適用確認
- name: Test hotfix
  ansible.windows.win_shell: |
    Get-HotFix | Where-Object { $_.HotFixID -eq "{{ item.name }}" }
  changed_when: false
  register: result
  failed_when: result.stdout == ""
  loop: "{{ win_hostfix_list }}"

# ホスト名確認
- name: Test hostname
  ansible.windows.win_shell: cmd /C hostname | Select-String "{{ inventory_hostname }}"
  changed_when: false
  register: result
  failed_when: result.stdout == ""

# 役割と機能の確認
- name: Test install windows features
  ansible.windows.win_shell: |
    Get-WindowsFeature | Select-Object Name, Installed | Where-Object { $_.Name -eq "{{ item }}" -and $_.Installed -eq $true }
  loop: "{{ win_feature_install_list }}"
  changed_when: false
  register: result
  failed_when: result.stdout == ""

# サービス無効化確認
- name: Test disable service
  ansible.windows.win_shell: |
    Get-Service | Select-Object Name, StartType, Status | Where-Object { $_.Name -eq "{{ item }}" -and $_.StartType -eq "Disabled" }
  loop: "{{ win_service_disable_list }}"
  changed_when: false
  register: result
  failed_when: result.stdout == ""

# サービス有効化確認
- name: Test enable service
  ansible.windows.win_shell: |
    Get-Service | Select-Object Name, StartType, Status | Where-Object { $_.Name -eq "{{ item }}" -and $_.StartType -eq "Automatic" }
  loop: "{{ win_service_enable_list }}"
  changed_when: false
  register: result
  failed_when: result.stdout == ""

# インタフェースのIPv6無効化確認
- name: Test disable ipv6
  ansible.windows.win_shell: |
    Get-NetAdapterBinding | Where-Object { $_.ComponentID -eq "ms_tcpip6" -and $_.Enabled -eq $false }
  changed_when: false
  register: result
  failed_when: result.stdout == ""

# 時刻同期確認
# 最終正常同期時刻がテスト日時と同一(「時」まで一致)であることを確認
- name: Test ntp sync
  ansible.windows.win_shell: w32tm /query /status | Select-String ((Get-Date).ToString("yyyy/MM/dd H"))
  changed_when: false
  register: result
  failed_when: result.stdout == ""
  until: result.stdout != ""
  retries: 6
  delay: 20

# ドメイン参加確認
- name: Test domain
  ansible.windows.win_shell: |
    Get-WmiObject Win32_ComputerSystem | Select-Object Name, Domain | Where-Object { $_.Domain -eq "{{ win_domain.name }}" }
  changed_when: false
  register: result
  failed_when: result.stdout == ""

- name: Test domain (Check DNS lookup)
  ansible.windows.win_shell: |
    Resolve-DnsName (Get-WmiObject Win32_ComputerSystem).Name
  changed_when: false
  register: result

# イベントログ設定確認
- name: Test eventlog settings (MaxSize)
  ansible.windows.win_shell: |
    Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\{{ item }}' | `
    Select-Object PrimaryModule, MaxSize, Retention | Where-Object { $_.MaxSize -eq {{ win_eventlog.max_size }} }
  changed_when: false
  register: result
  failed_when: result.stdout == ""
  loop:
    - System
    - Application
    - Security

# イベントを上書きする:0、上書きしない:4294967295
- name: Test eventlog settings (Retention)
  ansible.windows.win_shell: |
    if("{{ win_eventlog.retention }}" -eq "OverwriteAsNeeded"){
      Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\{{ item }}' | `
      Select-Object PrimaryModule, MaxSize, Retention | Where-Object { $_.Retention -eq 0 }
    }else{
      Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\{{ item }}' | `
      Select-Object PrimaryModule, MaxSize, Retention | Where-Object { $_.Retention -eq 4294967295 }
    }
  changed_when: false
  register: result
  failed_when: result.stdout == ""
  loop:
    - System
    - Application
    - Security

roles/setup_windows_server/handlers/main.yml : ハンドラーのタスク本体

ハンドラーでは、Hotfix適用後のNASアンマウントを行う。

タスク名 設定
Unmount nfs volume Hotfixを保存したNASをアンマウントする。AnsibleコントロールノードにてNASを一度だけアンマウントするよう、run_once: trueを設定する。
---
- name: Unmount nfs volume
  ansible.posix.mount:
    path: /ansible_mnt
    state: absent
  delegate_to: localhost
  run_once: true

ファイルの内容の説明は以上となる。このPlaybookを実際に実行してみよう。

Playbook実行結果

全処理実行

Playbookの実行結果を以下に記載する。

# ansible-playbook -i staging site.yml 

PLAY [Setup windows server] ***********************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Include build tasks] *************************************************************************************************
included: /root/ansible/setup_windows_server/roles/setup_windows_server/tasks/build.yml for t1194w219

TASK [setup_windows_server : Set timezone] ********************************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Create local users] **************************************************************************************************
changed: [t1194w219] => (item=None)
ok: [t1194w219] => (item=None)
changed: [t1194w219]

TASK [setup_windows_server : Mount nfs volume] ****************************************************************************************************
ok: [t1194w219 -> localhost]

TASK [setup_windows_server : Copy hotfix files] ***************************************************************************************************
changed: [t1194w219]

TASK [setup_windows_server : Install hotfix] ******************************************************************************************************
changed: [t1194w219] => (item={'name': 'kb5005112', 'path': 'C:\\temp\\windows10.0-kb5005112-x64_81d09dc6978520e1a6d44b3b15567667f83eba2c.msu'})

TASK [setup_windows_server : Set hostname] ********************************************************************************************************
changed: [t1194w219]

TASK [setup_windows_server : Reboot after changed hostname] ***************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Wait for reboot] *****************************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Install windows feature] *********************************************************************************************
changed: [t1194w219]

TASK [setup_windows_server : Disable service] *****************************************************************************************************
changed: [t1194w219] => (item=AxInstSV)

TASK [setup_windows_server : Enable service] ******************************************************************************************************
ok: [t1194w219] => (item=W32Time)

TASK [setup_windows_server : Disable ipv6 of network adapter] *************************************************************************************
changed: [t1194w219]

TASK [setup_windows_server : Join domain] *********************************************************************************************************
changed: [t1194w219]

TASK [setup_windows_server : Reboot after joined domain] ******************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Wait for reboot] *****************************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Set eventlog settings] ***********************************************************************************************
ok: [t1194w219] => (item=System)
ok: [t1194w219] => (item=Application)
ok: [t1194w219] => (item=Security)

TASK [setup_windows_server : Include unit test tasks] *********************************************************************************************
included: /root/ansible/setup_windows_server/roles/setup_windows_server/tasks/unit_test.yml for t1194w219

TASK [setup_windows_server : Test timezone] *******************************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Test local users] ****************************************************************************************************
ok: [t1194w219] => (item=None)
ok: [t1194w219] => (item=None)
ok: [t1194w219]

TASK [setup_windows_server : Test hotfix] *********************************************************************************************************
ok: [t1194w219] => (item={'name': 'kb5005112', 'path': 'C:\\temp\\windows10.0-kb5005112-x64_81d09dc6978520e1a6d44b3b15567667f83eba2c.msu'})

TASK [setup_windows_server : Test hostname] *******************************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Test install windows features] ***************************************************************************************
ok: [t1194w219] => (item=Windows-Server-Backup)

TASK [setup_windows_server : Test disable service] ************************************************************************************************
ok: [t1194w219] => (item=AxInstSV)

TASK [setup_windows_server : Test enable service] *************************************************************************************************
ok: [t1194w219] => (item=W32Time)

TASK [setup_windows_server : Test disable ipv6] ***************************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Test ntp sync] *******************************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Test domain] *********************************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Test domain (Check DNS lookup)] **************************************************************************************
ok: [t1194w219]

TASK [setup_windows_server : Test eventlog settings (MaxSize)] ************************************************************************************
ok: [t1194w219] => (item=System)
ok: [t1194w219] => (item=Application)
ok: [t1194w219] => (item=Security)

TASK [setup_windows_server : Test eventlog settings (Retention)] **********************************************************************************
ok: [t1194w219] => (item=System)
ok: [t1194w219] => (item=Application)
ok: [t1194w219] => (item=Security)

PLAY RECAP ****************************************************************************************************************************************
t1194w219                  : ok=32   changed=8    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

0 件のコメント:

コメントを投稿

人気の投稿