告别手写 Inventory:Terraform 与 Ansible 在 Azure 上的联动,动态清单自动接管云主机

一、为什么要把Terraform和Ansible 放在一起看

很多人会把 Terraform 和 Ansible 分开学:

  • Terraform 用来创建云资源
  • Ansible 用来配置服务器

单独看都没问题,但真正落地时,问题往往出在两者怎么接起来。

最常见的低效做法是:

  1. 先用 Terraform 创建 Azure 虚拟机
  2. 再手动整理 IP
  3. 再手写 Ansible inventory
  4. 机器一变更,inventory 又得改

这套流程最大的问题就是:基础设施已经自动化了,主机清单还是半手工维护。

Ansible 官方提供的 azure.azcollection.azure_rm 动态清单插件,就是为了解决这个问题:

它可以直接从 Azure Resource Manager 查询虚拟机信息,不再依赖手工维护静态 hosts 文件,在主机经常变化的云环境中,减少维护静态 inventory 的负担。

所以这篇文章是一个思路:

Terraform 负责创建 Azure 资源并打标签,Ansible 通过 Azure 动态清单按标签自动发现并分组,最后执行配置。


二、整体架构思路

整个流程可以概括成下面四步:

  1. Terraform 创建 Azure VM、网卡、公网 IP、NSG 等资源
  2. Terraform 在 VM 上打标签,例如:
    • env=dev
    • role=web
    • app=demo
  3. Ansible 使用 azure_rm 动态清单插件,从 Azure 拉取虚拟机信息
  4. Ansible Playbook 按标签生成的主机组执行配置任务

这种方式的好处是:

  • 不需要手工维护 inventory
  • 扩容新主机后,Ansible 下次执行自动发现
  • 可以按环境、角色、应用自动分组
  • Terraform 和 Ansible 的职责边界更清晰

三、为什么推荐"动态清单 + 标签分组",而不是 Terraform 直接生成 inventory 文件

Terraform 确实可以通过 local_file 在本地生成 inventory 文件。

这种方式适合简单 demo,但不适合作为主方案。云主机 IP、数量、角色都可能变化。 如果 inventory 是 Terraform 渲染出来的静态文件,那它仍然是一份"产物文件",而不是实时视图。

如果换一台机器执行 Terraform,而本地文件不存在,Terraform 会把它识别为需要重建的资源,这容易带来额外 diff 噪音。

我知道有provisioner 这个东西,但是Terraform 官方也明确建议: provisioner 应该是最后手段,因为 Terraform 的核心职责是管理资源生命周期,而不是承担复杂的后置配置逻辑。

因此,在 Azure 场景下,更推荐的方式是:

  • Terraform:创建 VM 并打好标签
  • Ansible:通过 Azure 动态清单实时读取 Azure 里的VM
  • Playbook:按标签生成的分组执行配置

四、准备工作

Azure 动态清单依赖 Ansible 的 Azure Collection。

azure.azcollection.azure_rm 是官方支持的 Azure Resource Manager inventory 插件,需要在执行 Ansible 的控制节点上安装相应 collection 和依赖。该插件要求 inventory 配置文件名以 azure_rm.yml 或 azure_rm.yaml 结尾。

1. 安装Ansible与Azure Collection

bash 复制代码
sudo apt update
sudo apt install -y pipx
pipx ensurepath
pipx install ansible
ansible-galaxy collection install azure.azcollection
pipx runpip ansible install -r ~/.local/share/pipx/venvs/ansible/lib/python*/site-packages/ansible_collections/azure/azcollection/requirements.txt

实际 requirements.txt 路径可能因安装方式不同而略有差异,可先用 ansible-galaxy collection list 和 python -c 等方式确认安装目录。

2. 准备Azure 凭证

azure_rm 支持多种认证方式,包括 auto、env、credential_file、cli、msi。

本地测试时可以直接使用 Azure CLI 登录;

bash 复制代码
az login
az account set --subscription "<your-subscription-id>"
``

CI/CD 或自动化环境更推荐服务主体等非交互式方式。

例如使用环境变量:

bash 复制代码
export AZURE_SUBSCRIPTION_ID="your_subscription_id"
export AZURE_CLIENT_ID="your_client_id"
export AZURE_SECRET="your_client_secret"
export AZURE_TENANT="your_tenant_id"

五、Terraform:创建Azure VM并打标签

下面示例不是完整生产模板,只演示关键思想:

Terraform 创建虚拟机时,把后续 Ansible 需要的分组信息提前写进标签。

json 复制代码
resource "azurerm_linux_virtual_machine" "web" {
  name                = "vm-web-01"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  size                = "Standard_B2s"
  admin_username      = "azureuser"
  network_interface_ids = [
    azurerm_network_interface.web_nic.id
  ]

  admin_ssh_key {
    username   = "azureuser"
    public_key = file("~/.ssh/id_rsa.pub")
  }

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts"
    version   = "latest"
  }

  tags = {
    env  = "dev"
    role = "web"
    app  = "demo"
  }
}

这里最重要的不是 VM 本身,而是 tags。

因为后面 Ansible 动态清单就可以基于这些标签自动分组。

六、使用Azure动态清单自动发现主机

inventory目录下创建一个文件,名称必须以 azure_rm.yml 结尾,例如:

yaml 复制代码
plugin: azure.azcollection.azure_rm #指定 Azure 动态清单插件
auth_source: auto
include_vm_resource_groups:  #限制查询范围,避免全订阅扫描,效率更高
  - rg-demo-dev
plain_host_names: yes #避免默认 inventory 主机名过长

#按标签自动生成组
keyed_groups:  
  - prefix: tag
    key: tags
  - prefix: azure_loc
    key: location

#显式生成web、dev 这样的逻辑组
conditional_groups:
  web: tags.role is defined and tags.role == 'web'
  dev: tags.env is defined and tags.env == 'dev'

#控制 ansible_host 取公网IP 或私网IP
hostvar_expressions:
  ansible_host: (public_ipv4_addresses + private_ipv4_addresses) | first

验证一下:

bash 复制代码
ansible-inventory -i inventory/myazure_rm.yml --graph

如果 Terraform 创建的 VM 已经存在且认证没问题,你就应该能看到按标签或条件生成的主机组。

七、让Terraform和Ansible 联动起来

terraform apply 后,Azure 里已经有了带标签的 VM;

Ansible 运行时直接去 Azure 读取当前状态。

也就是说,两者之间的"桥梁"不是一个中间文件,而是 Azure 资源本身的标签元数据。

这样做有几个好处:

  • Terraform 扩容一台新 role=web 的 VM 后,Ansible 下次执行会自动把它归入 web 组
  • 如果某台主机删除了,它也会从动态清单里自然消失
  • 不需要写额外的 inventory 渲染逻辑
  • 不需要担心静态 hosts 文件和真实云资源状态不一致

八、示例 playbook

例如你要给所有 web 主机安装 Nginx,在playbooks目录创建web.yml:

yaml 复制代码
- name: Configure web servers
  hosts: web
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.package:
        name: nginx
        state: present

    - name: Ensure nginx is running
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

执行方式:

bash 复制代码
ansible-playbook -i inventory/myazure_rm.yml playbooks/web.yml \
  -u azureuser --private-key ~/.ssh/id_rsa

如果 Terraform 后续又新增了两台 role=web 的虚拟机,只要标签一致,Ansible 不需要改 inventory,就能自动把它们纳入执行范围。

相关推荐
做运维的阿瑞1 天前
云项目可观测性与成本治理:告警、日志、Dashboard 与预算标签
大数据·云原生·云计算·terraform
广目软件1 天前
HA分布式架构采集节点变更操作手册20261029
运维·服务器·ansible·zabbix·prometheus
做运维的阿瑞1 天前
EKS 可销毁环境:集群创建、应用部署到账单归零
云原生·云计算·terraform
Moshow郑锴2 天前
Ansible 批量部署 Node.js 服务:ZIP 直解与共享目录分发两种方案对比
node.js·ansible
垚垚学技术_聚焦云原生2 天前
Ansible Playbook 进阶:when 条件、loop 循环、handlers 触发器落地
linux·ansible·自动化运维·devops
Keith Guo2 天前
What does OTA TAB mean?
azure
Moshow郑锴2 天前
GCP MIG 重建后 Ansible 初始化实战:PowerShell + NSSM 挂载 Windows 服务
windows·ansible
Keith Guo4 天前
What does device twin mean in azure cloud?
azure·iot
Keith Guo4 天前
What does IoT Central raw data mean?
物联网·azure·iot
承渊政道4 天前
新增一台服务器时还要重新装Nginx?Ansible只改hosts目标列表,同一份Playbook继续执行
运维·nginx·ansible·内网穿透·cpolar