解析ansible远程管理客户端【win终端为例】
一、前提:
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机器管理模块
# 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: http://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: https://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,参数说明https://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: https://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
执行结束,已完成。