动态清单插件

动态清单插件允许用户指向数据源来编译 Ansible 用于定位任务的主机清单,方法是通过 -i /path/to/file 和/或 -i 'host1, host2' 命令行参数或其他配置源。

在使用 Ansible 与 AWS 时,清单文件的维护将是一项繁琐的任务,因为 AWS 经常更改 IP 地址、自动扩展实例等等。一旦您的 AWS EC2 主机启动,您可能需要再次与它们通信。对于云设置,最好不要在文本文件中维护云主机名的静态列表。相反,最好的方法是使用 aws_ec2 动态清单插件。

aws_ec2 动态清单插件会向 AWS 发出 API 调用,以在运行时从 Amazon Web Services EC2 获取主机清单列表。它动态地提供 EC2 实例详细信息,以管理 AWS 基础设施。

该插件还将返回在 Ansible 之外创建的实例,并允许 Ansible 管理它们。

要开始使用具有 YAML 配置源的 aws_ec2 动态清单插件,请创建一个文件,其中包含为该插件记录的可接受的文件名模式(以 aws_ec2.(yml|yaml) 结尾的 YAML 配置文件,例如 demo.aws_ec2.yml),然后添加 plugin: amazon.aws.aws_ec2。如果插件位于集合中,请使用完全限定名称。

身份验证

如果您的 Ansible 控制器不在 AWS 中,则身份验证通过指定您的访问密钥和密钥作为环境变量或清单插件参数来处理。

对于环境变量

export AWS_ACCESS_KEY_ID='AK123'
export AWS_SECRET_ACCESS_KEY='abc123'

还可以使用 AWS_SECURITY_TOKEN 环境变量,但这仅为向后兼容性而支持。AWS_SECURITY_TOKENAWS_SESSION_TOKEN 的替代品,仅当您使用临时凭据时才需要它。

或者,您可以在清单配置文件中设置 aws_access_keyaws_secret_keysecurity_token

# demo.aws_ec2.yml
plugin: amazon.aws.aws_ec2

# The access key for your AWS account.
aws_access_key: <YOUR-AWS-ACCESS-KEY-HERE>
# The secret access key for your AWS account.
aws_secret_key: <YOUR-AWS-SECRET-KEY-HERE>

如果您对不同的工具或应用程序使用不同的凭据,则可以使用配置文件。

profile 参数与 aws_access_keyaws_secret_keysecurity_token 选项互斥。当没有明确提供凭据时,Ansible 使用的 AWS SDK (boto3) 将回退到其配置文件(通常为 ~/.aws/credentials)。共享凭据文件的默认位置为 ~/.aws/credentials。您可以通过设置 AWS_SHARED_CREDENTIALS_FILE 环境变量来更改共享凭据文件的位置。

# demo.aws_ec2.yml
plugin: amazon.aws.aws_ec2

# Attach the default AWS profile
aws_profile: default

# You could use Jinja2 to attach the AWS profile from the environment variable.
aws_profile: "{{ lookup('env', 'AWS_PROFILE') | default('dev-profile', true) }}"

您也可以将您的 AWS 配置文件设置为环境变量

export AWS_PROFILE='test-profile'

如果您的 Ansible 控制器在分配了 IAM 角色的 EC2 实例上运行,则可以省略凭据。有关更多详细信息,请参阅控制器的文档 了解更多详情

您还可以使用 IAM 角色的 ARN 来假定执行清单查找。这对于跨不同帐户连接或限制用户访问非常有用。为此,您应该指定 iam_role_arn。您仍然应该提供具有足够权限来执行 AssumeRole 操作的 AWS 凭据。

# demo.aws_ec2.yml
plugin: amazon.aws.aws_ec2

iam_role_arn: arn:aws:iam::1234567890:role/assumed-ansible

最小示例

获取 us-east-1 中的所有主机,主机名如果存在则为公共 DNS,否则为私有 IP 地址。

# demo.aws_ec2.yml
plugin: amazon.aws.aws_ec2

# This sets the region. If empty (the default) default this will include all regions, except possibly
# restricted ones like us-gov-west-1 and cn-north-1.
regions:
- us-east-1

提供任何必需的选项后,您可以使用 ansible-inventory -i demo.aws_ec2.yml --graph 查看填充的清单。

@all:
 |--@aws_ec2:
 |  |--ip-10-210-0-189.ec2.internal
 |  |--ip-10-210-0-195.ec2.internal
 |--@ungrouped:

允许的选项

下面详细解释了部分 aws_ec2 动态清单插件选项。有关完整列表,请参阅 插件文档

hostnames

hostnames 选项提供不同的设置来选择如何显示主机名。

下面显示一些示例

hostnames:
  # This option allows displaying the public ip addresses.
  - ip-address

  # This option allows displaying the private ip addresses using `tag:Name` as a prefix.
  # `name` can be one of the options specified in http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html#options.
  - name: 'private-ip-address'
    separator: '_'
    prefix: 'tag:Name'

  # Using literal values for hostname
  # # Hostname will be aws-test_literal
  - name: 'test_literal'
    separator: '-'
    prefix: 'aws'

  # To use tags as hostnames use the syntax `tag:Name=Value` to use the hostname `Name_Value`, or
  # `tag:Name` to use the value of the Name tag. If value provided does not exist in the above options,
  # it will be used as a literal string.
  - name: 'tag:Tag1=Test1,Tag2=Test2'

  # Use dns-name attribute as hostname
  - dns-name

  # You can also specify a list in order of precedence for hostname variables.
  - ip-address
  - dns-name
  - tag:Name
  - private-ip-address

默认情况下,清单将仅返回 hostnames 条目中的第一个匹配项。您可能希望在清单中获得所有可能的匹配项,这也意味着您将获得重复的条目。要切换到此行为,请将 allow_duplicated_hosts 配置键设置为 True

keyed_groups

您可以使用主机变量和 keyed_groups 选项创建动态组。keyed_groups 采用前缀和键的格式。前缀将是要与键连接的主机组的名称。

下面显示一些示例

keyed_groups:
# This creates host groups based on architecture.
- prefix: arch
  key: architecture

# This creates host groups based on `x86_64` architecture.
- prefix: arch
  key: architecture
  value:
      'x86_64'

# This creates host groups based on availability zone.
- prefix: az
  key: placement.availability_zone

# If the EC2 tag Name had the value `redhat` the tag variable would be: `tag_Name_redhat`.
# Similarly, if a tag existed for an AWS EC2 instance as `Applications` with the value of `nodejs` the
# variable would be: `tag_Applications_nodejs`.
- prefix: tag
  key: tags

# This creates host groups using instance_type, e.g., `instance_type_z3_tiny`.
- prefix: instance_type
  key: instance_type

# This creates host groups using security_groups id, e.g., `security_groups_sg_abcd1234` group for each security group.
- key: 'security_groups|json_query("[].group_id")'
  prefix: 'security_groups'

# This creates a host group for each value of the Application tag.
- key: tags.Application
  separator: ''

# This creates a host group per region e.g., `aws_region_us_east_2`.
- key: placement.region
  prefix: aws_region

# This creates host groups based on the value of a custom tag `Role` and adds them to a metagroup called `project`.
- key: tags['Role']
  prefix: foo
  parent_group: "project"

# This creates a common parent group for all EC2 availability zones.
- key: placement.availability_zone
  parent_group: all_ec2_zones

# This creates a group per distro (distro_CentOS, distro_Debian) and assigns the hosts that have matching values to it,
# using the default separator "_".
- prefix: distro
  key: ansible_distribution

groups

也可以使用 groups 选项创建组。

下面显示一些示例

groups:
  # This created two groups - `Production` and `PreProduction` based on tags
  # These conditionals are expressed using Jinja2 syntax.
  redhat: "'Production' in tags.Environment"
  ubuntu: "'PreProduction' in tags.Environment"

  # This created a libvpc group based on specific condition on `vpc_id`.
  libvpc: vpc_id == 'vpc-####'

compose

compose 从 Jinja2 表达式创建和修改主机变量。

compose:
  # This sets the ansible_host variable to connect with the private IP address without changing the hostname.
  ansible_host: private_ip_address

  # This sets location_vars variable as a dictionary with location as a key.
  location_vars:
    location: "east_coast"
    server_type: "ansible_hostname | regex_replace ('(.{6})(.{2}).*', '\\2')"

  # This sets location variable.
  location: "'east_coast'"

  # This lets you connect over SSM to the instance id.
  ansible_host: instance_id
  ansible_connection: 'community.aws.aws_ssm'

  # This defines combinations of host servers, IP addresses, and related SSH private keys.
  ansible_host: private_ip_address
  ansible_user: centos
  ansible_ssh_private_key_file: /path/to/private_key_file

  # This sets the ec2_security_group_ids variable.
  ec2_security_group_ids: security_groups | map(attribute='group_id') | list |  join(',')

  # Host variables that are strings need to be wrapped with two sets of quotes.
  # See https://docs.ansible.org.cn/ansible/latest/plugins/inventory.html#using-inventory-plugins for details.
  ansible_connection: '"community.aws.aws_ssm"'
  ansible_user: '"ssm-user"'

include_filtersexclude_filters

include_filtersexclude_filters 选项允许您使用多个查询来构建清单(参见 可用过滤器)。

include_filters:
# This includes everything in the inventory that has the following tags.
- tag:Project:
    - 'planets'
- tag:Environment:
    - 'demo'

# This excludes everything from the inventory that has the following tag:Name.
exclude_filters:
- tag:Name:
    - '{{ resource_prefix }}_3'

filters

filters 用于根据条件过滤 AWS EC2 实例(参见 可用过滤器)。

filters:
  # This selects only running instances with tag `Environment` tag set to `dev`.
  tag:Environment: dev
  instance-state-name : running

  # This selects only instances with tag `Environment` tag set to `dev` and `qa` and specific security group id.
  tag:Environment:
    - dev
    - qa
  instance.group-id: sg-xxxxxxxx

  # This selects only instances with tag `Name` fulfilling specific conditions.
  - tag:Name:
    - dev-*
    - share-resource
    - hotfix

use_contrib_script_compatible_ec2_tag_keysuse_contrib_script_compatible_sanitization

use_contrib_script_compatible_ec2_tag_keys 为 True 时,它会像旧的 ec2.py 清单脚本一样公开带有 ec2_tag_TAGNAME 密钥的主机标签。

默认情况下,aws_ec2 插件使用通用组名规范化来创建安全且可用的组名以供 Ansible 使用。

use_contrib_script_compatible_ec2_tag_keys 允许您覆盖此设置,以方便从旧的清单脚本迁移,并在脚本的 replace_dash_in_groups 选项设置为 False 时匹配组的规范化。要使用构造的组复制 replace_dash_in_groups = True 的行为,您需要使用 regex_replace 过滤器将这些条目的连字符替换为下划线。

为此,您还应关闭 TRANSFORM_INVALID_GROUP_CHARS 设置,否则核心引擎将只在其之上使用标准规范化。

这不是默认设置,因为这样的名称会破坏某些功能,因为并非所有字符都是有效的 Python 标识符,而组名最终会被用作 Python 标识符。

不建议使用此功能,我们建议迁移到新的标签结构。

# demo.aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
filters:
  tag:Name:
  - 'instance-*'
hostnames:
- tag:Name
use_contrib_script_compatible_sanitization: True
use_contrib_script_compatible_ec2_tag_keys: True

提供任何必需的选项后,您可以使用 ansible-inventory -i demo.aws_ec2.yml --list 查看已填充的清单。

{
  "_meta": {
      "hostvars": {
          "instance-01": {
              "aws_ami_launch_index_ec2": 0,
              "aws_architecture_ec2": "x86_64",
              ...
              "ebs_optimized": false,
              "ec2_tag_Environment": "dev",
              "ec2_tag_Name": "instance-01",
              "ec2_tag_Tag1": "Test1",
              "ec2_tag_Tag2": "Test2",
              "ena_support": true,
              "enclave_options": {
                  "enabled": false
              },
              ...
          },
          "instance-02": {
            ...
            "ebs_optimized": false,
            "ec2_tag_Environment": "dev",
            "ec2_tag_Name": "instance-02",
            "ec2_tag_Tag1": "Test3",
            "ec2_tag_Tag2": "Test4",
            "ena_support": true,
            "enclave_options": {
                "enabled": false
            },
            ...
          }
      }
  },
  all": {
        "children": [
            "aws_ec2",
            "ungrouped"
        ]
    },
    "aws_ec2": {
        "hosts": [
            "instance-01",
            "instance-02"
        ]
    }
}

hostvars_prefixhostvars_suffix

hostvars_prefixhostvars_sufix 允许为主机变量设置前缀和后缀。

# demo.aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
filters:
  tag:Name:
  - 'instance-*'
hostvars_prefix: 'aws_'
hostvars_suffix: '_ec2'
hostnames:
- tag:Name

现在是 ansible-inventory -i demo.aws_ec2.yml --list 的输出

{
  "_meta": {
      "hostvars": {
          "instance-01": {
              "aws_ami_launch_index_ec2": 0,
              "aws_architecture_ec2": "x86_64",
              "aws_block_device_mappings_ec2": [
                  {
                      "device_name": "/dev/sda1",
                      "ebs": {
                          "attach_time": "2022-06-27T09:04:57+00:00",
                          "delete_on_termination": true,
                          "status": "attached",
                          "volume_id": "vol-06e065bca44e6eae5"
                      }
                  }
              ],
              "aws_capacity_reservation_specification_ec2": {
                  "capacity_reservation_preference": "open"
              }
              ...,
          },
          "instance-02": {
            ...,
          }
      }
  },
  all": {
        "children": [
            "aws_ec2",
            "ungrouped"
        ]
    },
    "aws_ec2": {
        "hosts": [
            "instance-01",
            "instance-02"
        ]
    }
}

strictstrict_permissions

strict: False 将跳过缺失的事实,而不是产生错误。

strict_permissions: False 将忽略 403 错误,而不是失败。

use_ssm_inventory

use_ssm_inventory: True 可将 AWS Systems Manager (SSM) 清单服务中的其他 EC2 实例信息提取到 hostvars 中。通过利用 SSM 清单数据,use_ssm_inventory 选项提供了关于清单中 EC2 实例的更多详细信息和属性。这些详细信息可能包括操作系统信息、已安装的软件、网络配置以及在 SSM 中定义的自定义清单属性。

cache

aws_ec2 清单插件支持缓存,可以使用在 ansible.cfg 文件的 [defaults] 部分中定义的事实缓存的通用设置,或在 [inventory] 部分中定义清单特定的设置。您可以在配置文件中定义插件特定的缓存设置。

# demo.aws_ec2.yml
plugin: aws_ec2
# This enables cache.
cache: yes
# Plugin to be used.
cache_plugin: jsonfile
cache_timeout: 7200
# Location where files are stored in the cache.
cache_connection: /tmp/aws_inventory
cache_prefix: aws_ec2

这是一个使用一些事实缓存默认值(用于缓存插件和超时)在 ansible.cfg 文件中设置清单缓存的示例。

[defaults]
fact_caching = ansible.builtin.jsonfile
fact_caching_connection = /tmp/ansible_facts
cache_timeout = 3600

[inventory]
cache = yes
cache_connection = /tmp/ansible_inventory

复杂示例

这是一个使用前面列出的一些选项的 aws_ec2 复杂示例。

# demo.aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
  - us-east-2
keyed_groups:
  # add hosts to tag_Name_value groups for each aws_ec2 host's tags.Name variable.
  - key: tags.Name
    prefix: tag_Name_
    separator: ""
groups:
  # add hosts to the group dev if any of the dictionary's keys or values is the word 'dev'.
  development: "'dev' in (tags|list)"
filters:
  tag:Name:
    - 'instance-01'
    - 'instance-03'
include_filters:
- tag:Name:
  - 'instance-02'
  - 'instance-04'
exclude_filters:
- tag:Name:
  - 'instance-03'
  - 'instance-04'
hostnames:
  # You can also specify a list in order of precedence for hostname variables.
  - ip-address
  - dns-name
  - tag:Name
  - private-ip-address
compose:
  # This sets the `ansible_host` variable to connect with the private IP address without changing the hostname.
  ansible_host: private_ip_address

如果主机没有上述配置中的变量(即 tags.Nametagsprivate_ip_address),则主机将不会添加到清单插件创建的组以外的组中,并且 ansible_host 主机变量将不会被修改。

现在是 ansible-inventory -i demo.aws_ec2.yml --graph 的输出。

@all:
|--@aws_ec2:
|  |--instance-01
|  |--instance-02
|--@tag_Name_instance_01:
|  |--instance-01
|--@tag_Name_instance_02:
|  |--instance-02
|--@ungrouped:

在剧本中使用动态清单

如果您想在剧本中使用动态清单,您只需要在 hosts 变量中提及组名,如下所示。

---
- name: Ansible Test Playbook
  gather_facts: false
  hosts: tag_Name_instance_02

  tasks:
    - name: Run Shell Command
      command: echo "Hello World"