解析ansible遠程管理客戶端【win終端為例】
- 2022 年 3 月 18 日
- 筆記
- Ansible, Ansible-playbook
一、前提:
1.1、windows機器開啟winrm服務,並設置成允許遠程連接狀態
具體操作命令如下
set-executionpolicy remotesigned
winrm quickconfig
#配置auth
winrm set winrm/config/service/auth '@{Basic="true"}'
#為winrm service 配置加密方式為允許非加密
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
在windows powershell中運行
1.2、準備條件
-
-
底層通訊基於 powershell,版本需要在4.0以上 $PSVersionTable.PSVersion獲取版本號 或者 get-host | findstr version
- 遠程windows機器開啟winrm
二、ansible管理機部署安裝
安裝ansible: yum -y install ansible 如果沒有安裝pip, 請先安裝對應於你的Python版本的pip: easy_install pip 以下的Python模組也需要安裝: pip install PyYAML 配置hosts文件: cat /etc/ansible/hosts [windows] 10.0.0.1 ansible_ssh_user="Administrator" ansible_ssh_pass="123456" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore 10.0.0.1 是windows伺服器的IP。 /etc/ansible/hosts 中看可添加多個windows伺服器的資訊 ,可集體一次性管理,分發任務。 至此,ansible服務端配置完畢。
2.2、windows被控制機器為win 10
winrm service 默認都是未啟用的狀態,先查看狀態;如無返回資訊,則是沒有啟動; winrm enumerate winrm/config/listener
針對winrm service 進行配置
針對winrm service 進行配置
winrm winrm quickconfig
# 輸入 y 回車
為 winrm 配置 auth
# 為winrm service 配置auth:
winrm set winrm/config/service/auth @{Basic="true"}
為winrm service 配置加密方式為允許非加密
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
關閉防火牆,並查看5985埠是否開啟,並在監聽中
netstat -ano | findstr 5985
2.3、測試ansible和客戶端的連接性與穩定性
windows下可用調試模組 win_ping
ansible -i hosts all -m win_ping
顯示連接成功
SUCCESS => {
"changed": false,
"ping": "pong"
windows下可用傳送文件模組 win_copy
ansible -i hosts all -m win_copy -a 'src=/etc/passwd dest=D:\passwd'
刪除D:\passwd ansible -i hosts all -m win_file -a "path=D:\passwd state=absent" 獲取ip地址 ansible -i hosts all -m raw -a "ipconfig" 獲取window主機資訊: ansible -i hosts all -m setup 創建文件夾: ansible -i hosts all -m raw -a 'md D:\dir' 移動文件: ansible -i hosts all -m raw -a "cmd /c 'move /y D:\Software\wmi_export.exe D:\wmi_export.exe'"
三、具體windows機器管理模組
ansible中 windows 和 linux 機器模組大同小異,windows除了模組名字前面多個 「win_」,除去這個後完全可以用於linux。
# Test connectivity to a windows host
# ansible winserver -m win_ping
- name: Example from an Ansible Playbook
win_ping:
- name: Induce an exception to see what happens # 異常查看
win_ping:
data: crash
3.2、win_command — 在win節點上執行口令
- name: Save the result of 'whoami' in 'whoami_out'
win_command: whoami
register: whoami_out
- name: Run command that only runs if folder exists and runs from a specific folder
win_command: wbadmin -backupTarget:C:\backup\
args:
chdir: C:\somedir\
creates: C:\backup\
- name: Run an executable and send data to the stdin for the executable
win_command: powershell.exe
args:
stdin: Write-Host test
3.3、win_shell — 在節點執行口令
與 win_command 不同的是,支援管道符,用powershell和cmd可以執行的命令,均可以批量執行
eg: ansible winserver -m win_shell -a “ipconfig | findstr IPv4”
# Execute a command in the remote shell; stdout goes to the specified
# file on the remote.
- win_shell: C:\somescript.ps1 >> C:\somelog.txt
# Change the working directory to somedir/ before executing the command.
- win_shell: C:\somescript.ps1 >> C:\somelog.txt chdir=C:\somedir
# You can also use the 'args' form to provide the options. This command
# will change the working directory to somedir/ and will only run when
# somedir/somelog.txt doesn't exist.
- win_shell: C:\somescript.ps1 >> C:\somelog.txt
args:
chdir: C:\somedir
creates: C:\somelog.txt
# Run a command under a non-Powershell interpreter (cmd in this case)
- win_shell: echo %HOMEDIR%
args:
executable: cmd
register: homedir_out
# 查看環境變數這一步由於linux/profile的影響,不識別 「 % 」,故使用如下語法方可獲取:
# ansible winserver -m raw -a 'echo $ENV:PATH'
- name: Run multi-lined shell commands
win_shell: |
$value = Test-Path -Path C:\temp
if ($value) {
Remove-Item -Path C:\temp -Force
}
New-Item -Path C:\temp -ItemType Directory
- name: Retrieve the input based on stdin
win_shell: '$string = [Console]::In.ReadToEnd(); Write-Output $string.Trim()'
args:
stdin: Input message
- name: Touch a file (creates if not present, updates modification time if present)
win_file:
path: C:\Temp\foo.conf
state: touch
- name: Remove a file, if present
win_file:
path: C:\Temp\foo.conf
state: absent
- name: Create directory structure
win_file:
path: C:\Temp\folder\subfolder
state: directory
- name: Remove directory structure
win_file:
path: C:\Temp
state: absent
# 將linux端的文件拷貝到win機器上,並修改文件名
- name: Copy a single file
win_copy:
src: /srv/myfiles/foo.conf
dest: C:\Temp\renamed-foo.conf
# 拷貝文件到指定位置並修改名稱和備份
- name: Copy a single file, but keep a backup
win_copy:
src: /srv/myfiles/foo.conf
dest: C:\Temp\renamed-foo.conf
backup: yes
# force=yes 文件重名是使用,參數表示為強制覆蓋文件
# 設置遠程主機文件的內容
- name: Set the contents of a file
win_copy:
content: abc123
dest: C:\Temp\foo.txt
# 將單個文件複製給另一個用戶
- name: Copy a single file as another user
win_copy:
src: NuGet.config
dest: '%AppData%\NuGet\NuGet.config'
vars:
ansible_become_user: user
ansible_become_password: pass
# The tmp dir must be set when using win_copy as another user
# This ensures the become user will have permissions for the operation
# Make sure to specify a folder both the ansible_user and the become_user have access to (i.e not %TEMP% which is user specific and requires Admin)
ansible_remote_tmp: 'c:\tmp'
3.6、win_package — 安裝或卸載安裝包
# 安裝 VC
- name: Install the Visual C thingy
win_package:
path: //download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe
product_id: '{CF2BEA3C-26EA-32F8-AA9B-331F7E34BA97}'
arguments: /install /passive /norestart
# 安裝遠程桌面 msi文件到遠程主機
- name: Install Remote Desktop Connection Manager from msi
win_package:
path: //download.microsoft.com/download/A/F/0/AF0071F3-B198-4A35-AA90-C68D103BDCCF/rdcman.msi
product_id: '{0240359E-6A4C-4884-9E94-B397A02D893C}'
state: present
# 卸載時只需要產品ID
- name: Uninstall Remote Desktop Connection Manager
win_package:
product_id: '{0240359E-6A4C-4884-9E94-B397A02D893C}'
state: absent
# 從本地MSI卸載遠程桌面連接管理器,省略product_id
- name: Uninstall Remote Desktop Connection Manager from local MSI omitting the product_id
win_package:
path: C:\temp\rdcman.msi
state: absent
# 7-Zip exe doesn't use a guid for the Product ID
- name: Install 7zip from a network share specifying the credentials
win_package:
path: \\domain\programs\7z.exe
product_id: 7-Zip
arguments: /S
state: present
user_name: DOMAIN\User
user_password: Password
# 安裝Python,參數說明//docs.python.org/3/using/windows.html
# InstallAllUsers:執行系統範圍的安裝 PrependPath:添加到環境變數 Include_pip:安裝pip
- name: 安裝Python3.9
win_package:
path: "python-3.9.2-amd64.exe"
state: present
product_id: python-3.9.2
arguments: /quiet InstallAllUsers=1 PrependPath=1 Include_test=0 Include_pip=1
3.7、win_firewall — 開啟或關閉防火牆
- name: Enable firewall for Domain, Public and Private profiles
win_firewall:
state: enabled
profiles:
- Domain
- Private
- Public
tags: enable_firewall
- name: Disable Domain firewall
win_firewall:
state: disabled
profiles:
- Domain
tags: disable_firewall
3.8、win_hostname — 管理本地windows機器名
- name: Change the hostname to sample-hostname
win_hostname:
name: sample-hostname
register: res
# 修改主機名後需要重啟windows才能生效
- name: Reboot
win_reboot:
when: res.reboot_required
3.9、win_scheduled_task — 管理計劃的任務
name 必填項 字元串
path (必填項) 通往ExecAction的可執行文件的路徑
description 任務的描述
triggers 觸發條件
# restart_count 任務調度器嘗試重新啟動任務的次數
# 如果已設置,則還必須設置 `restart_count`,最長允許時間為31天,最短允許時間為1分鐘。
# 這是在ISO 8601持續時間格式 `P[n]Y[n]M[n]DT[n]H[n]M[n]S`
# run_only_if_idle 是否只有在電腦處於空閑狀態時才會運行任務
- name: Create a task that will be repeated every minute forever
win_scheduled_task:
name: keepalive
description: keep alive the windows.exe
actions:
- path: C:\crontab\crontab.ps1
triggers:
- type: registration
repetition:
interval: PT2M # 每兩分鐘執行一次
duration: PT5M # 持續五分鐘
stop_at_duration_end: no # 任務的運行實例是否在重複模式結束時停止
username: "administrator" # 給指定用戶創建定時任務
3.10、win_regedit — 添加、更改或刪除註冊表項和值
- name: Obtain information about a registry key using short form
ansible.windows.win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
register: current_version
- name: Obtain information about a registry key property
ansible.windows.win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
name: CommonFilesDir
register: common_files_dir
- name: Obtain the registry key's (Default) property
ansible.windows.win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
name: ''
register: current_version_default
# LimitBlankPasswordUse
# 0 = enable empty passwords on network
# 1 = disable empty passwords on network
- name: Modify the registry so that the specified user can log in with an empty password
win_regedit:
path: HKLM:\SYSTEM\CurrentControlSet\Control\Lsa
name: LimitBlankPasswordUse
data: 0
type: dword
- name: 創建指定用戶
win_user:
name: zhoujt
password: B0bP4ssw0rd # 設置密碼,不加此項的話默認為沒有密碼
password_never_expires: yes # 密碼用不過期
state: present # 創建用戶
groups: # 用戶歸屬組
- Users # 默認組
- Administrators # 添加為管理員
# 創建Jinja2模板,並傳輸到伺服器
- name: Create a file from a Jinja2 template
win_template:
src: /mytemplates/file.conf.j2 # src去template找對應jinja2文件
dest: C:\Temp\file.conf # 推送到伺服器後,變更文件名和後綴,方可達到傳送配置文件的效果
3.13、win_environment 在Win主機上修改環境變數
- name: Set an environment variable for all users
win_environment:
state: present
name: TestVariable
value: Test value
level: machine
- name: Remove an environment variable for the current user
win_environment:
state: absent
name: TestVariable
level: user
# 添加環境變數到系統用戶 Path路徑下
- raw: echo $ENV:PATH
register: path_out
- name: 為所有用戶設置需要添加應用的環境變數
win_environment:
state: present
name: Path
# value:將path的全部拿下來,替換後再添加需要添加的程式路徑
value: "{{ path_out.stdout | regex_replace('[\r\n]*', '')}} + ;C:\\windows\\win64"
level: machine # 系統級別
- name: Set several variables at once
win_environment:
level: machine
variables:
TestVariable: Test value
CUSTOM_APP_VAR: 'Very important value'
ANOTHER_VAR: '{{ my_ansible_var }}'
- name: Set and remove multiple variables at once
win_environment:
level: user
variables:
TestVariable: Test value
CUSTOM_APP_VAR: 'Very important value'
ANOTHER_VAR: '{{ my_ansible_var }}'
UNWANTED_VAR: '' # < this will be removed
- name: Install the foo service
community.windows.win_nssm:
name: foo
application: C:\windows\foo.exe
# This will yield the following command: C:\windows\foo.exe bar "true"
- name: Install the Consul service with a list of parameters
community.windows.win_nssm:
name: Consul
application: C:\consul\consul.exe
arguments:
- agent
- -config-dir=C:\consul\config
# This is strictly equivalent to the previous example
- name: Install the Consul service with an arbitrary string of parameters
community.windows.win_nssm:
name: Consul
application: C:\consul\consul.exe
arguments: agent -config-dir=C:\consul\config
# Install the foo service, and then configure and start it with win_service
- name: Install the foo service, redirecting stdout and stderr to the same file
community.windows.win_nssm:
name: foo
application: C:\windows\foo.exe
stdout_file: C:\windows\foo.log
stderr_file: C:\windows\foo.log
- name: Configure and start the foo service using win_service
ansible.windows.win_service:
name: foo
dependencies: [ adf, tcpip ]
username: foouser
password: secret
start_mode: manual
state: started
- name: Install a script based service and define custom environment variables
community.windows.win_nssm:
name: <ServiceName>
application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- <path-to-script>
- <script arg>
app_environment:
AUTH_TOKEN: <token value>
SERVER_URL: //example.com
PATH: "<path-prepends>;{{ ansible_env.PATH }};<path-appends>"
- name: Remove the foo service
community.windows.win_nssm:
name: foo
state: absent
3.15、win_path — 管理windows環境變數
- name: Ensure that system32 and Powershell are present on the global system path, and in the specified order
win_path:
elements:
- '%SystemRoot%\system32'
- '%SystemRoot%\system32\WindowsPowerShell\v1.0'
- name: Ensure that C:\Program Files\MyJavaThing is not on the current user's CLASSPATH
win_path:
name: class
elements: C:\Program Files\MyJavaThing
scope: machine
state: absent
# This unzips a library that was downloaded with win_get_url, and removes the file after extraction
# $ ansible -i hosts -m win_unzip -a "src=C:\LibraryToUnzip.zip dest=C:\Lib remove=yes" all
- name: Unzip a bz2 (BZip) file
win_unzip:
src: C:\Users\Phil\Logs.bz2
dest: C:\Users\Phil\OldLogs
creates: C:\Users\Phil\OldLogs # 如果這個文件或文件夾存在,指定的會src失效
- name: unzip windows.zip to Remote Host
win_unzip:
src: "{{ windir }}\\windows.zip"
dest: "{{ windir }}"
delete_archive: yes # 在解壓縮完成後,刪除壓縮文件 (默認是 no)
# Unzip .zip file, recursively decompresses the contained .gz files and removes all unneeded compressed files after completion.
- name: Unzip ApplicationLogs.zip and decompress all GZipped log files
hosts: all
gather_facts: no
tasks:
- name: Recursively decompress GZ files in ApplicationLogs.zip
win_unzip:
src: C:\Downloads\ApplicationLogs.zip
dest: C:\Application\Logs
recurse: yes
delete_archive: yes
- name: Install PSCX
win_psmodule:
name: Pscx
state: present
linux 解壓和windows大同小異
- name: Decompress the installation package to the target address of the server
unarchive:
src: "{{ PushDir }}/{{ PackName.stdout }}"
dest: "{{ UnzipDir }}"
remote_src: yes # 將遠端伺服器上的壓縮包解壓到遠程伺服器
- name: Extract foo.tgz into /var/lib/foo
unarchive:
src: foo.tgz
dest: /var/lib/foo
- name: Unarchive a file that is already on the remote machine
unarchive:
src: /tmp/foo.zip
dest: /usr/local/bin
remote_src: yes # 取消歸檔遠程伺服器已經存在的文件
- name: print to stdout
command: echo "hello"
register: hello
- debug: msg="{{ hello.stdout }}" # 返回正常輸出
- debug: msg="{{ hello.stderr }}" # 返回異常輸出
3.18、tags 標籤
vim tags1.yaml
- hosts: webserver
remote_user: root
gather_facts: no # 關閉收集facts變數
tasks:
- name: copy hosts file
copy: src=/etc/hosts dest=/opt/hosts
tags:
- only
- name: touch file
file: path=/opt/touch1 state=touch
tags:
- tpath
在調用win_shell模組中,將shell模組的返回值資訊申請一個新的註冊名稱,後續的debug任務可通過該註冊的任務名稱判斷這個win_shell模組的執行狀態,如遇到win_shell執行失敗的時候我們可以用 “ignore_errors: true” ,用來掌控如果執行失敗後也能執行後面的任務。並且我們可以為win_shell模組添加判斷條件,當不滿足條件時,win_shell模組會自動跳過,當然,也可以修改一下條件,以便測試skip的判斷結果。
在ansible中,”is exists”表示如果路徑存在於ansible節點則返回真,”is not exists”表示如果路徑不存在於ansible節點則返回真,當我們使用一種tests進行條件判斷時,在tests前面加上”is”進行判斷,也可以在tests前面加上”is not”進行取反的判斷。
判斷變數
defined :判斷變數是否已經定義,已經定義則返回真
undefind :判斷變數是否已經定義,未定義則返回真
none :判斷變數值是否為空,如果變數已經定義,但是變數值為空,則返回真
判斷執行結果
success 或 succeeded:通過任務的返回資訊判斷任務的執行狀態,任務執行成功則返回真
failure 或 failed:通過任務的返回資訊判斷任務的執行狀態,任務執行失敗則返回真
change 或 changed:通過任務的返回資訊判斷任務的執行狀態,任務執行狀態為changed則返回真
skip 或 skipped:通過任務的返回資訊判斷任務的執行狀態,當任務沒有滿足條件,而被跳過執行時,則返回真
判斷路徑
註:判斷均針對於ansible主機中的路徑,與目標主機無關
file : 判斷路徑是否是一個文件,如果路徑是一個文件則返回真
directory :判斷路徑是否是一個目錄,如果路徑是一個目錄則返回真
其他的判斷
link :判斷路徑是否是一個軟鏈接,如果路徑是一個軟鏈接則返回真
mount:判斷路徑是否是一個掛載點,如果路徑是一個掛載點則返回真
exists:判斷路徑是否存在,如果路徑存在則返回真
string:判斷對象是否是一個字元串,是字元串則返回真
number:判斷對象是否是一個數字,是數字則返回真
subset:判斷一個list是不是另一個list的子集,是另一個list的子集時返回真
superset : 判斷一個list是不是另一個list的父集,是另一個list的父集時返回真
lower:判斷包含字母的字元串中的字母是否是純小寫,字元串中的字母全部為小寫則返回真
upper:判斷包含字母的字元串中的字母是否是純大寫,字元串中的字母全部為大寫則返回真
even :判斷數值是否是偶數,是偶數則返回真
odd :判斷數值是否是奇數,是奇數則返回真
divisibleby(num) :判斷是否可以整除指定的數值(num),如果除以指定的值以後餘數為0,則返回真
eg:
- debug:
msg: "It's divisible"
when: num is divisibleby(10)
舉例:
- name: Determine if the registry has a key
win_shell: "REG QUERY 'HKCU\\Control Panel\\windows' /v windows"
register: ifexits
ignore_errors: true
tags:
- haskey
- debug:
msg: "when there's this {{ ifexits.stdout }}"
when: ifexits is success
tags:
- haskey
- name: Delete key-value pairs
win_shell: "REG DELETE 'HKCU\\Control Panel\\windows' /v windows /f"
args:
executable: powershell
when: ifexits is success # 當結果為執行成功時執行這個task
tags:
- haskey
在playbook執行過程中,ansible收集facts變數是很耗時的一個步驟,如果我們確定play中沒有用到fact變數資訊,可以直接將其關閉
關閉獲取 facts 很簡單,只需要在 playbook 文件中加上「gather_facts: no」即可
Forks可以用來設置並行的主機數,默認值為5,在生產中,多數情況下我們會修改這個參數,需要在控制節點的CPU和網路性能上支援,設置20+是可以的
在 ansible.cfg 中設置forks的全局默認值 find / -name "ansible.cfg" cat /etc/ansible/ansible.cfg [defaults] forks = 15
Serial 用來控制一個play內的主機並行數,這個並行數不能超過 forks ,超過後Serial就不會生效
-- - hosts: winserver serial: 10 tasks:
4.3、ansible 排障
- vvvv # 用來查看ansible的執行流程
4.4、條件判斷
is not exists # 不存在 is exists # 存在 取反
五、ansible-roles
5.1、創建roles
需要注意的是,功能放在roles目錄下,執行劇本放在roles同級目錄下
[zhoujt@Huawei ~/ansible/roles ]$ ansible-galaxy init zhouceshi
- Role zhouceshi was created successfully
[zhoujt@Huawei ~/ansible/roles ]$ tree zhoujt/
zhoujt/
|-- defaults # 存放默認的變數
| `-- main.yml
|-- files # ansible中 file copy等模組會自動來這裡找文件,在執行roles的時候我們只需要寫文件名
|-- handlers # 存放 tasks 中的notify的指定內容
| `-- main.yml
|-- meta # 存放playbook的目錄
| `-- main.yml
|-- README.md
|-- tasks # 存放主要執行的執行體,按照順序在 main.yml 中 用include 排序,依次運行
| `-- main.yml
|-- templates # 存放jinja2模板,用來將文件推送到指定機器的自定義文件
|-- tests # 用於存放測試role本身功能的playbook和主機定義文件,在開發測試階段比較常用
| |-- inventory
| `-- test.yml
|-- vars # 存放變數
`-- main.yml
5.2、運行roles
roles可以理解為每個不同的role代表不同的功能
[zhoujt@Huawei ~/ansible/roles ]$ cat zhoujtinit.yml
- hosts: winserver # 執行該role的用戶組
vars: # 變數最高級別,高於role/zhouceshi/vars/main.yml
vars1url: 'access.baidu.com'
vars2dir: "C:\\System\\Package\\"
vars3name: 'zhoujt'
vars4user: 'admin'
roles: # 需要執行的roles,可寫多role同時執行
- role: zhouceshi
- role: zhouceshi1
[zhoujt@Huawei ~/ansible/roles ]$ ansible-playbook zhoujtinit.yml
PLAY [winserver] ************************************************************************************************************************
TASK [Create a task that will be repeated every minute forever] *************************************************************************
ok: [winserver]
PLAY RECAP ******************************************************************************************************************************
winserver1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可以使用標籤,用來測試roles,或者跳過指定任務
列出標籤 ansible-playbook --list-tags testtag.yml 跳過指定標籤 ansible-playbook --skip-tags only testtag.yml
執行結束,已完成。


