本文介绍如何使用 Ansible 在 Azure 中部署 Windows Server 2019 VM。
在这篇文章中,你将学会如何:
- 创建资源组
 - 创建虚拟网络、公共 IP、网络安全组和网络接口
 - 部署 Windows Server 虚拟机
 - 通过 WinRM 连接到虚拟机
 - 运行 Ansible playbook 以配置 Windows IIS
 
先决条件
- Azure 订阅:如果没有 Azure 订阅,请在开始之前创建 一个免费帐户 。
 
- Azure 服务主体: 创建服务主体,记下以下值: appId、 displayName、 密码和 租户。
 
安装 Ansible:执行以下选项之一:
- 在 Linux 虚拟机上安装和 配置 Ansible
 - 配置 Azure Cloud Shell
 
向 Ansible 添加 WinRM 支持
若要通过 WinRM 进行通信,Ansible 控制服务器需要 python 包 pywinrm。
在 Ansible 服务器上运行以下命令以安装 pywinrm:
pip install "pywinrm>=0.3.0"
有关详细信息,请参阅 适用于 Ansible 的 Windows 远程管理。
创建资源组
创建一个名为 azure_windows_vm.yml 的 Ansible 剧本,并将以下内容复制到该剧本中:
---
- name: Create Azure VM
  hosts: localhost
  connection: local
  tasks:
  - name: Create resource group
    azure_rm_resourcegroup:
      name: myResourceGroup
      location: eastus
要点:
- 将 
hosts设为 localhost ,在 Ansible 服务器上直接本地运行 playbook。 
创建虚拟网络和子网
将以下任务添加到 azure_windows_vm.yml Ansible playbook 以创建虚拟网络:
  - name: Create virtual network
    azure_rm_virtualnetwork:
      resource_group: myResourceGroup
      name: vNet
      address_prefixes: "10.0.0.0/16"
  - name: Add subnet
    azure_rm_subnet:
      resource_group: myResourceGroup
      name: subnet
      address_prefix: "10.0.1.0/24"
      virtual_network: vNet
创建公共 IP 地址
将以下任务添加到 azure_windows_vm.yml playbook 以创建公共 IP 地址:
  - name: Create public IP address
    azure_rm_publicipaddress:
      resource_group: myResourceGroup
      allocation_method: Static
      name: pip
    register: output_ip_address
  - name: Output public IP
    debug:
      msg: "The public IP is {{ output_ip_address.state.ip_address }}"
要点:
- Ansible 
register模块用于将azure_rm_publicipaddress的输出存储在名为output_ip_address的变量中。 - 该 
debug模块用于将 VM 的公共 IP 地址输出到控制台。 
创建网络安全组和 NIC
网络安全组定义了允许和不允许到达虚拟机的流量。
若要打开 WinRM 和 HTTP 端口,请将以下任务添加到 azure_windows_vm.yml Ansible playbook:
  - name: Create Network Security Group
    azure_rm_securitygroup:
      resource_group: myResourceGroup
      name: networkSecurityGroup
      rules:
        - name: 'allow_rdp'
          protocol: Tcp
          destination_port_range: 3389
          access: Allow
          priority: 1001
          direction: Inbound
        - name: 'allow_web_traffic'
          protocol: Tcp
          destination_port_range:
            - 80
            - 443
          access: Allow
          priority: 1002
          direction: Inbound
        - name: 'allow_powershell_remoting'
          protocol: Tcp
          destination_port_range: 
            - 5985
            - 5986
          access: Allow
          priority: 1003
          direction: Inbound
  - name: Create a network interface
    azure_rm_networkinterface:
      name: nic
      resource_group: myResourceGroup
      virtual_network: vNet
      subnet_name: subnet
      security_group: networkSecurityGroup
      ip_configurations:
        - name: default
          public_ip_address_name: pip
          primary: True
要点:
- 虚拟网络接口卡将 VM 连接到其虚拟网络、公共 IP 地址和安全组。
 - 
              
azure_rm_securitygroup创建了一个 Azure 网络安全组,用于允许端口5985和端口5986,以便允许从 Ansible 服务器到远程主机的 WinRM 流量。 
创建虚拟机
接下来,创建使用本文前面部分创建的所有资源的虚拟机。
将以下任务添加到 azure_windows_vm.yml Ansible playbook:
  - name: Create VM
    azure_rm_virtualmachine:
      resource_group: myResourceGroup
      name: win-vm
      vm_size: Standard_DS1_v2
      admin_username: azureuser
      admin_password: "{{ password }}"
      network_interfaces: nic
      os_type: Windows
      image:
          offer: WindowsServer
          publisher: MicrosoftWindowsServer
          sku: 2019-Datacenter
          version: latest
    no_log: true
              admin_password值为{{ password }}包含 Windows VM 密码的 Ansible 变量。 若要安全地填充该变量,请将条目 var_prompts 添加到 playbook 的开头。
- name: Create Azure VM
  hosts: localhost
  connection: local
  vars_prompt:
    - name: password
      prompt: "Enter local administrator password"
  tasks:
要点:
- 避免将敏感数据存储为纯文本。 使用
var_prompts在运行时填充变量。 添加no_log: true以防止密码记录。 
配置 WinRM 侦听器
Ansible 使用 PowerShell 通过 WinRM 连接和配置 Windows 远程主机。
若要配置 WinRM,请添加以下 ext azure_rm_virtualmachineextension:
  - name: Create VM script extension to enable HTTPS WinRM listener
    azure_rm_virtualmachineextension:
      name: winrm-extension
      resource_group: myResourceGroup
      virtual_machine_name: win-vm
      publisher: Microsoft.Compute
      virtual_machine_extension_type: CustomScriptExtension
      type_handler_version: '1.9'
      settings: '{"fileUris": ["https://raw.githubusercontent.com/ansible/ansible-documentation/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"],"commandToExecute": "powershell -ExecutionPolicy Unrestricted -File ConfigureRemotingForAnsible.ps1"}'
      auto_upgrade_minor_version: true
在完全配置 WinRM 之前,Ansible 无法连接到 VM。
请在 playbook 中添加以下任务以等待 WinRM 连接:
  - name: Get facts for one Public IP
    azure_rm_publicipaddress_info:
      resource_group: myResourceGroup
      name: pip
    register: publicipaddresses
  - name: set public ip address fact
    set_fact: publicipaddress="{{ publicipaddresses | json_query('publicipaddresses[0].ip_address')}}"
  - name: wait for the WinRM port to come online
    wait_for:
      port: 5986
      host: '{{ publicipaddress }}'
      timeout: 600
要点:
- 该 
azure_rm_virtualmachineextension模块允许在本地 Azure Windows 上运行 PowerShell 脚本。ConfigureRemotingForAnsible.ps1运行 PowerShell 脚本通过创建自签名证书并打开 Ansible 连接所需的端口来配置 WinRM。 - 该 
azure_rm_publicipaddress_info模块查询 Azure 中的公共 IP 地址,然后将set_fact输出存储在变量中供wait_for模块使用。 
完整示例 Ansible 剧本
本节列出了你在本文过程中已构建的整个 Ansible playbook 示例。
---
- name: Create Azure VM
  hosts: localhost
  connection: local
  vars_prompt:
    - name: password
      prompt: "Enter local administrator password"
  tasks:
  - name: Create resource group
    azure_rm_resourcegroup:
      name: myResourceGroup
      location: eastus
  - name: Create virtual network
    azure_rm_virtualnetwork:
      resource_group: myResourceGroup
      name: vNet
      address_prefixes: "10.0.0.0/16"
  - name: Add subnet
    azure_rm_subnet:
      resource_group: myResourceGroup
      name: subnet
      address_prefix: "10.0.1.0/24"
      virtual_network: vNet
  - name: Create public IP address
    azure_rm_publicipaddress:
      resource_group: myResourceGroup
      allocation_method: Static
      name: pip
    register: output_ip_address
  - name: Output public IP
    debug:
      msg: "The public IP is {{ output_ip_address.state.ip_address }}"
  
  - name: Create Network Security Group
    azure_rm_securitygroup:
      resource_group: myResourceGroup
      name: networkSecurityGroup
      rules:
        - name: 'allow_rdp'
          protocol: Tcp
          destination_port_range: 3389
          access: Allow
          priority: 1001
          direction: Inbound
        - name: 'allow_web_traffic'
          protocol: Tcp
          destination_port_range:
            - 80
            - 443
          access: Allow
          priority: 1002
          direction: Inbound
        - name: 'allow_powershell_remoting'
          protocol: Tcp
          destination_port_range: 
            - 5985
            - 5986
          access: Allow
          priority: 1003
          direction: Inbound
  - name: Create a network interface
    azure_rm_networkinterface:
      name: nic
      resource_group: myResourceGroup
      virtual_network: vNet
      subnet_name: subnet
      security_group: networkSecurityGroup
      ip_configurations:
        - name: default
          public_ip_address_name: pip
          primary: True
  - name: Create VM
    azure_rm_virtualmachine:
      resource_group: myResourceGroup
      name: win-vm
      vm_size: Standard_DS1_v2
      admin_username: azureuser
      admin_password: "{{ password }}"
      network_interfaces: nic
      os_type: Windows
      image:
          offer: WindowsServer
          publisher: MicrosoftWindowsServer
          sku: 2019-Datacenter
          version: latest
    no_log: true
  - name: Create VM script extension to enable HTTPS WinRM listener
    azure_rm_virtualmachineextension:
      name: winrm-extension
      resource_group: myResourceGroup
      virtual_machine_name: win-vm
      publisher: Microsoft.Compute
      virtual_machine_extension_type: CustomScriptExtension
      type_handler_version: '1.9'
      settings: '{"fileUris": ["https://raw.githubusercontent.com/ansible/ansible-documentation/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"],"commandToExecute": "powershell -ExecutionPolicy Unrestricted -File ConfigureRemotingForAnsible.ps1"}'
      auto_upgrade_minor_version: true
  - name: Get facts for one Public IP
    azure_rm_publicipaddress_info:
      resource_group: myResourceGroup
      name: pip
    register: publicipaddresses
  - name: set public ip address fact
    set_fact: publicipaddress="{{ publicipaddresses | json_query('publicipaddresses[0].ip_address')}}"
  - name: wait for the WinRM port to come online
    wait_for:
      port: 5986
      host: '{{ publicipaddress }}'
      timeout: 600
连接到 Windows 虚拟机
创建一个名为connect_azure_windows_vm.yml的新 Ansible 剧本,并将以下内容复制到该剧本中:
---
- hosts: all
  vars_prompt:
    - name: ansible_password
      prompt: "Enter local administrator password"
  vars:
    ansible_user: azureuser
    ansible_connection: winrm
    ansible_winrm_transport: ntlm
    ansible_winrm_server_cert_validation: ignore
  tasks:
  - name: Test connection
    win_ping:
运行 Ansible 剧本。
ansible-playbook connect_azure_windows_vm.yml -i <publicIPaddress>,
将 <publicIPaddress> 替换为您的虚拟机地址。
要点:
- Ansible 的配置确定 Ansible 如何连接和向远程主机进行身份验证。 若要连接到 Windows 主机,需要定义的变量取决于 WinRM 连接类型和所选的身份验证选项。 有关详细信息,请参阅 “连接到 Windows 主机 ”和 “Windows 身份验证选项”。
 - 在公共 IP 地址后面添加逗号会绕过 Ansible 的清单分析程序。 通过此技术,无需清单文件即可运行剧本。
 
清理资源
将以下代码保存为
delete_rg.yml.--- - hosts: localhost tasks: - name: Deleting resource group - "{{ name }}" azure_rm_resourcegroup: name: "{{ name }}" state: absent register: rg - debug: var: rg使用 ansible-playbook 命令来运行 playbook。 将占位符替换为要删除的资源组的名称。 资源组中的所有资源都将被删除。
ansible-playbook delete_rg.yml --extra-vars "name=<resource_group>"要点:
- 因为 playbook 中的
register变量和debug部分,命令完成后会显示结果。 
- 因为 playbook 中的