a story

ansible - windows 모듈 (2) 본문

IaC

ansible - windows 모듈 (2)

한명 2022. 2. 4. 00:10

지난 번에 이어 몇가지 모듈을 더 살펴 보겠습니다.

 

(6) 명령 수행

특정 명령을 수행하는 시나리오 입니다.

[root@labco7ans ansible]# cat 07.command.yml
---
- hosts: win
  gather_facts: no
  tasks:
  - name: run an executable using win_command
    win_command: whoami.exe

  - name: run a cmd command
    win_command: cmd.exe /c mkdir C:\test
    
[root@labco7ans ansible]# ansible-playbook -i hosts 07.command.yml

PLAY [win] ***********************************************************************************************************************************************************

TASK [run an executable using win_command] ***************************************************************************************************************************
changed: [172.16.3.106]

TASK [run a cmd command] *********************************************************************************************************************************************
changed: [172.16.3.106]

PLAY RECAP ***********************************************************************************************************************************************************
172.16.3.106               : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

생성되었습니다.

PS C:\> dir

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
<생략>
d-----        6/29/2020   4:33 PM                Temp
d-----        6/30/2020  10:35 AM                test
d-r---        6/29/2020   4:46 PM                Users
d-----        6/29/2020   4:45 PM                Windows

그런데 이를 한번 더 실행하면 어떨까요?

[root@labco7ans ansible]# ansible-playbook -i hosts 07.command.yml

PLAY [win] ***********************************************************************************************************************************************************

TASK [run an executable using win_command] ***************************************************************************************************************************
changed: [172.16.3.106]

TASK [run a cmd command] *********************************************************************************************************************************************
fatal: [172.16.3.106]: FAILED! => {"changed": true, "cmd": "cmd.exe /c mkdir C:\\test", "delta": "0:00:00.140629", "end": "2020-06-30 01:35:51.440674", "msg": "non-zero return code", "rc": 1, "start": "2020-06-30 01:35:51.300045", "stderr": "A subdirectory or file C:\\test already exists.\r\n", "stderr_lines": ["A subdirectory or file C:\\test already exists."], "stdout": "", "stdout_lines": []}

PLAY RECAP ***********************************************************************************************************************************************************
172.16.3.106               : ok=1    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

stderr로 "A subdirectory or file C:\\test already exists.\r\n" 이 발생합니다.

이러한 command는 멱등성의 단위가 아닌 별개의 작업으로 간주됩니다.

한가지 더 살펴볼 것은 앞서 살펴본 예제에서도 있던 register입니다. register 를 whoami.exe 결과를 받아오는데 사용될 수도 있습니다. 예제를 조금 수정했습니다.

[root@labco7ans ansible]# cat 07.command2.yml
---
- hosts: win
  gather_facts: no
  tasks:
  - name: run an executable using win_command
    win_command: whoami.exe
    register: output
  - debug: msg="{{ output.stdout }}"

[root@labco7ans ansible]# ansible-playbook -i hosts 07.command2.yml

PLAY [win] ***********************************************************************************************************************************************************

TASK [run an executable using win_command] ***************************************************************************************************************************
changed: [172.16.3.106]

TASK [debug] *********************************************************************************************************************************************************
ok: [172.16.3.106] => {
    "msg": "labw16ans\\administrator\r\n"
}

PLAY RECAP ***********************************************************************************************************************************************************
172.16.3.106               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

(7) 환경 변수 설정

시스템 환경변수를 추가하는 시나리오 입니다.

[root@labco7ans ansible]# cat 08.env.yml
---
- hosts: win
  gather_facts: no
  tasks:
  - name: Set an environment variable for all users
    win_environment:
      state: present
      name: NewVariable
      value: New Value
      level: machine

[root@labco7ans ansible]# ansible-playbook -i hosts 08.env.yml

PLAY [win] ***********************************************************************************************************************************************************

TASK [Set an environment variable for all users] *********************************************************************************************************************
changed: [172.16.3.106]

PLAY RECAP ***********************************************************************************************************************************************************
172.16.3.106               : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

아래와 같이 생성되었습니다.

C:\Users\User1>set |findstr New
NewVariable=New Value

Windows 서버의 환경변수는 시스템 환경변수와 사용자 환경변수로 나뉩니다. 이를 level로 정의합니다. 하기 참고하시기 바랍니다. 

공식문서의 Notes에서도 언급된 내용은, 이 module 자체가 변경사항을 전파하지 않는다는 점입니다(This module does not broadcast change events). 그러하므로 변경사항을 세션이나 시스템에 반영하기 위해 새로운 세션을 생성하거나 reboot이 필요할 수 있습니다.

참고: https://docs.ansible.com/ansible/latest/modules/win_environment_module.html#win-environment-module

 

자매품으로 win_path 모듈로 세부 element를 수정할 수 있습니다. 기본적인 환경변수 추가는 win_envrionment를 사용하시기 바랍니다.

참고: https://docs.ansible.com/ansible/latest/modules/win_path_module.html#win-path-module

 

(8) 레지스트리 관리

레지스트리를 관리하는 시나리오는 아래와 같습니다.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdhid\Parameters의 CrashOnCtrlScroll를 1로 세팅

[root@labco7ans ansible]# cat 09.reg.yml
---
- hosts: win
  gather_facts: no
  tasks:
  - name: Add or update registry with dword entry 'CrashOnCtrlScroll', and containing 1 as the hex value
    win_regedit:
      path: HKLM:\SYSTEM\CurrentControlSet\Services\Kbdhid\Parameters
      name: CrashOnCtrlScroll
      data: 0x1
      type: dword
      
[root@labco7ans ansible]# ansible-playbook -i hosts 09.reg.yml

PLAY [win] ***********************************************************************************************************************************************************

TASK [Add or update registry with dword entry 'CrashOnCtrlScroll', and containing 1 as the hex value] ****************************************************************
changed: [172.16.3.106]

PLAY RECAP ***********************************************************************************************************************************************************
172.16.3.106               : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

아래와 같이 값이 입력되었습니다.

<사전>
C:\>reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdhid\Parameters

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdhid\Parameters
    WorkNicely    REG_DWORD    0x0

<사후>
C:\>reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdhid\Parameters

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdhid\Parameters
    WorkNicely    REG_DWORD    0x0
    CrashOnCtrlScroll    REG_DWORD    0x1

다른 방법은 별도의 reg 파일을 작성 후 win_regmerge로 적용시킬 수도 있습니다. 이 경우 앞서 살펴본 win_copy로 필요한 reg 파일을 전달하고 적용시킬 수 있습니다.

참고: https://docs.ansible.com/ansible/latest/modules/win_regmerge_module.html#win-regmerge-module

 

(9) 페이지파일 설정

페이지 파일을 설정하는 시나리오입니다.

[root@labco7ans ansible]# ansible win -i hosts -m win_pagefile
172.16.3.106 | SUCCESS => {
    "automatic_managed_pagefiles": true,
    "changed": false,
    "pagefiles": []
}

[root@labco7ans ansible]# cat 10.pagefile.yml
---
- hosts: win
  gather_facts: no
  tasks:
  - name: Disable AutomaticManagedPagefile and set C pagefile
    win_pagefile:
      drive: C
      initial_size: 2048
      maximum_size: 2048
      automatic: no
      state: present

[root@labco7ans ansible]# ansible-playbook -i hosts 10.pagefile.yml

PLAY [win] ***********************************************************************************************************************************************************

TASK [Disable AutomaticManagedPagefile and set C pagefile] ***********************************************************************************************************
changed: [172.16.3.106]

PLAY RECAP ***********************************************************************************************************************************************************
172.16.3.106               : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@labco7ans ansible]# ansible win -i hosts -m win_pagefile
172.16.3.106 | SUCCESS => {
    "automatic_managed_pagefiles": false,
    "changed": false,
    "pagefiles": [
        {
            "caption": "C:\\ 'pagefile.sys'",
            "description": "'pagefile.sys' @ C:\\",
            "initial_size": 2048,
            "maximum_size": 2048,
            "name": "C:\\pagefile.sys"
        }
    ]
}

페이지 파일 세팅을 적용하기 위해 reboot이 필요할 수 있습니다. reboot 시나리오를 추가했습니다. pagefile 단계가 changed 상태가되면 reboot이 진행되는 시나리오입니다.

[root@labco7ans ansible]# cat 10.pagefile2.yml
---
- hosts: win
  gather_facts: no
  tasks:
  - name: Disable AutomaticManagedPagefile and set C pagefile
    win_pagefile:
      drive: C
      initial_size: 4096
      maximum_size: 4096
      automatic: no
      state: present
    register: pagefile

  - name: Reboot server
    win_reboot:
      msg: "Page file moved,rebooting..."
      pre_reboot_delay: 15
    when: pagefile.changed
    
[root@labco7ans ansible]# ansible-playbook -i hosts 10.pagefile2.yml

PLAY [win] ***********************************************************************************************************************************************************

TASK [Disable AutomaticManagedPagefile and set C pagefile] ***********************************************************************************************************
changed: [172.16.3.106]

TASK [Reboot server] *************************************************************************************************************************************************
changed: [172.16.3.106]

PLAY RECAP ***********************************************************************************************************************************************************
172.16.3.106               : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@labco7ans ansible]# ansible win -i hosts -m win_pagefile
172.16.3.106 | SUCCESS => {
    "automatic_managed_pagefiles": false,
    "changed": false,
    "pagefiles": [
        {
            "caption": "C:\\ 'pagefile.sys'",
            "description": "'pagefile.sys' @ C:\\",
            "initial_size": 4096,
            "maximum_size": 4096,
            "name": "C:\\pagefile.sys"
        }
    ]
}

 

(10) 로컬 보안 정책 관리

로컬 보안 정책(secpol)을 변경하는 시나리오입니다.

먼저 secedit을 export해서 변경하려는 값을 살펴 봅니다.

C:\>SecEdit.exe /export /cfg C:\temp\output.ini

The task has completed successfully.
See log %windir%\security\logs\scesrv.log for detail info.

C:\>type C:\temp\output.ini
[Unicode]
Unicode=yes
[System Access]
MinimumPasswordAge = 0
MaximumPasswordAge = 42
MinimumPasswordLength = 0
PasswordComplexity = 1
<생략>

MaximumPasswordAge 을 90으로 변경해 보는 시나리오를 수행해보겠습니다.

파일에서 확인한 내용으로 section에서 [System Access] 를 필요한 key와 value 를 각각 설정합니다.

[root@labco7ans ansible]# cat 11.secpol.yml
---
- hosts: win
  gather_facts: no
  tasks:
  - name: Set the maximum password age
    win_security_policy:
      section: System Access
      key: MaximumPasswordAge
      value: 90
      
[root@labco7ans ansible]# ansible-playbook -i hosts 11.secpol.yml

PLAY [win] ***********************************************************************************************************************************************************

TASK [Set the maximum password age] **********************************************************************************************************************************
changed: [172.16.3.106]

PLAY RECAP ***********************************************************************************************************************************************************
172.16.3.106               : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

아래와 같이 변경되었습니다.

C:\>SecEdit.exe /export /cfg C:\temp\output2.ini

The task has completed successfully.
See log %windir%\security\logs\scesrv.log for detail info.

C:\>type C:\temp\output2.ini | findstr Password
MinimumPasswordAge = 0
MaximumPasswordAge = 90
MinimumPasswordLength = 0
PasswordComplexity = 1
PasswordHistorySize = 0
<생략>

 

(11) 업데이트 적용

윈도우즈의 패치는 아래와 같이 진행할 수 있습니다.

- name: Install all critical and security updates
  win_updates:
    category_names:
    - CriticalUpdates
    - SecurityUpdates
    state: installed
  register: update_result

- name: Reboot host if required
  win_reboot:
  when: update_result.reboot_required

다만 Windows 서버의 업데이트는 약간의 복잡성을 가지고 있습니다. 상기의 시나리오는 MS나 WSUS 로의 업데이트가 가능한 환경이라는 전제가 되있어야합니다.

Air-gapped 환경이라면 아래와 같은 시나리오도 생각해 볼 수 있습니다.

  • 웹서버 세팅 > 업데이트 파일을 Docroot에 복사 > playbook (win_get_url + win_hotfix로 설치)

업데이트 파일 이동을 위해 win_copy를 생각할 수도 있겠지만 win_copy의 경우 WinRM 상에서 동작하므로 용량이 큰 경우 효과적이지 않습니다 (Because win_copy runs over WinRM, it is not a very efficient transfer mechanism. If sending large files consider hosting them on a web service and using [win_get_url](https://docs.ansible.com/ansible/latest/modules/win_get_url_module.html#win-get-url-module) instead.) 공식문서의 커맨트를 따라 win_get_url을 사용했습니다.

혹은 download.windowsupdate.com을 URL 방화벽으로 오픈이 가능하다면 아래와 같이 진행할 수도 있습니다.

- name: Download KB3172729 for Server 2012 R2
  win_get_url:
    url: http://download.windowsupdate.com/d/msdownload/update/software/secu/2016/07/windows8.1-kb3172729-x64_e8003822a7ef4705cbb65623b72fd3cec73fe222.msu
    dest: C:\temp\KB3172729.msu

- name: Install hotfix
  win_hotfix:
    hotfix_kb: KB3172729
    source: C:\temp\KB3172729.msu
    state: present
  register: hotfix_result

- name: Reboot host if required
  win_reboot:
  when: hotfix_result.reboot_required

마지막 시나리오로 SSU 업데이트를 적용해 보겠습니다.

[root@labco7ans ansible]# cat 12.update.yml
---
- hosts: win
  gather_facts: no
  tasks:
  - name: Download SSU(KB4550994) for Server 2016
    win_get_url:
      url: http://download.windowsupdate.com/c/msdownload/update/software/secu/2020/04/windows10.0-kb4550994-x64_1df8a8ea245041495f3b219fb22f3849908d8e27.msu
      dest: C:\temp\KB4550994.msu

  - name: Install hotfix
    win_hotfix:
      hotfix_kb: KB4550994
      source: C:\temp\KB4550994.msu
      state: present
    register: hotfix_result

  - name: Reboot host if required
    win_reboot:
    when: hotfix_result.reboot_required
    
[root@labco7ans ansible]# ansible-playbook -i hosts 12.update.yml

PLAY [win] ***********************************************************************************************************************************************************

TASK [Download SSU(KB4550994) for Server 2016] ***********************************************************************************************************************
changed: [172.16.3.106]

TASK [Install hotfix] ************************************************************************************************************************************************
changed: [172.16.3.106]

TASK [Reboot host if required] ***************************************************************************************************************************************
skipping: [172.16.3.106]

PLAY RECAP ***********************************************************************************************************************************************************
172.16.3.106               : ok=2    changed=2    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

SSU 패치라 reboot task는 skip 되었고, 아래 결과를 보면 KB4550994 가 설치된 것을 알 수 있습니다.

C:\>wmic qfe
Caption                                     CSName     Description      FixComments  HotFixID   InstallDate  InstalledBy              InstalledOn  Name  ServicePackInEffect  Status
http://support.microsoft.com/?kbid=3192137  LABW16ANS  Update                        KB3192137               NT AUTHORITY\SYSTEM      9/12/2016
http://support.microsoft.com/?kbid=4550994  LABW16ANS  Security Update               KB4550994               LABW16ANS\Administrator  6/30/2020

이를 마지막으로 Windows 서버에서 ansible을 활용해 사용해볼만한 Windows Module을 살펴봤습니다.

 

 

참고

https://docs.ansible.com/ansible/latest/modules/win_feature_module.html#win-feature-module

https://docs.ansible.com/ansible/latest/user_guide/windows_usage.html#use-cases

https://geekflare.com/ansible-playbook-windows-example/

'IaC' 카테고리의 다른 글

ansible - windows 모듈 (1)  (0) 2022.02.04