Issue about defining variable within the task does not work

124 views
Skip to first unread message

ricardo barbosa

unread,
Jan 13, 2024, 9:40:57 AM1/13/24
to ansible...@googlegroups.com
Hello guys

I created the following structure:

```
amis:
  us-east-1:
    amazonlinux:
      owner_id: "137112412989"
      x86_64: "ami-01bc990364452ab3e"
      arm64: "ami-0900a8f768a21540a"
      user: "ec2-user"
    ubuntu:
      owner_id: "099720109477"
      x86_64: "ami-0fc5d935ebf8bc3bc" # Ubuntu 22
      arm64: "ami-016485166ec7fa705"  #
      user: "ubuntu"
```

and I have a json file that I fill in to raise an ec2 instance:

```json
{
    "aws_region":"us-east-1",
    "architecture":"arm64",
    "os_version":"ubuntu",
    "instance_name":"test6",
    "domain":"example.corp",
    "subnet":"SUBNET-PUB-A",
    "instance_type":"t4g.micro",
    "security_groups": [ "sg-xxxxxx", "sg-yyyyyyyy"],
    "aws_role":"default-role",
    "root_volume_size":20,
    "ebs_swap_size": 4,
    "keyname": "ssh-key",
    "ebs_type": "gp3",
    "backup": "no",
    "boxenv":"DEV"
}
```

And I read this json and create the variables with the tasks:

```
- name: Read json configuration
    shell: cat config-ec2-launch.json
    register: result
     
  - name: save the Json data to a Variable as a Fact
    set_fact:
      jsondata: "{{ result.stdout | from_json }}"

  - name: Variable | Get aws_region variable
    set_fact:
      aws_region: "{{ jsondata | json_query(jmesquery) }}"
    vars:
      jmesquery: 'aws_region'
     
  - name: Variable | Get architecture variable
    set_fact:              
      architecture: "{{ jsondata | json_query(jmesquery) }}"
    vars:
      jmesquery: 'architecture'

  - name: Variable | Get os_version variable
    set_fact:
      os_version: "{{ jsondata | json_query(jmesquery) }}"
    vars:
      jmesquery: 'os_version'
```

and created the tasks right away:

```
- name: AWS Ec2 Instance | Create the EC2 instance
    ec2_instance:
      state: started   #  started state=running + waits for EC2 status checks to report OK if wait=true
      image_id: "{{ newest_ami }}"
      name: "{{ instance_name }}"
      detailed_monitoring: false
      metadata_options:
        http_tokens: required
      instance_type: "{{ instance_type }}"
      region: "{{ aws_region }}"
      vpc_subnet_id: "{{ subnet_facts.subnets.0.id }}"    
      instance_initiated_shutdown_behavior: stop
      instance_role: "{{ aws_roles[aws_role] }}"
      volumes:
        - device_name: "{{ name_device_root_ami.images.0.root_device_name }}"
          ebs:
            volume_type: gp3
            volume_size: "{{ root_volume_size }}"
            delete_on_termination: true
      security_groups: "{{ security_groups }}"
      tags:
        Name: "{{ instance_name }}"
        Domain: "{{ domain }}"
        Backup: "{{ backup }}"
        BOXENV: "{{ boxenv }}"
      key_name: "{{ aws_key_names[keyname] }}"
      wait: yes
    register: ec2
   
  - name: AWS Ec2 ebs | Add volume to swap
    amazon.aws.ec2_vol:
      instance: "{{ ec2.instances[0].instance_id }}"
      volume_size: "{{ ebs_swap_size }}"
      volume_type: "{{ ebs_type }}"
      delete_on_termination: True
      device_name: "/dev/sdf"
      region: '{{ aws_region }}'
      tags:
        Name: "{{ instance_name }}:swap"
        BOXENV: "{{ boxenv }}"
   

  - name: Swap | List all devices on {{ instance_name }}
    vars:
        ansible_ssh_user: "{{ amis[ aws_region ][ os_version ].user }}"
    ansible.builtin.shell: echo "{{ aws_region }} {{ ec2.instances.0.network_interfaces.0.private_ip_address }}"
    register: devices_host
    delegate_to: "{{ ec2.instances.0.network_interfaces.0.private_ip_address }}"
    args:
      executable: /bin/bash
```

But when I run it, it displays the error below

```
FAILED! => {"msg": "'aws_region' is undefined"}
```

If I comment the line `ansible_ssh_user: "{{ amis[ aws_region ][ os_version ].user }}"` it displays the contents of the aws_region variable. Any idea where I'm going wrong?

Thanks


Todd Lewis

unread,
Jan 13, 2024, 11:52:55 AM1/13/24
to Ansible Project
It's even stranger than that (until you realize what's going on).
Rather than commenting out the "ansible_ssh_user:" line, just misspell the variable, like "ansible_ssh_userx". It'll work then, too — but with the wrong user of course.

What you're running into is a combination of things: set_fact, delegate_to, and the bit you didn't show, which is your playbook's "hosts:" line and/or your command's "--limit" specification. Also the "special variable" function of the "ansible_ssh_user" variable itself. Maybe throw in lazy variable template evaluation for good measure.

The error message is correct but incomplete, in that "aws_region" is undefined. What the message leaves out is "… in the context of the delegation host 'ec2.instances.0.network_interfaces.0.private_ip_address'".

You set the task variable "ansible_ssh_user" to a template. By lazy template evaluation, that template isn't evaluated until the variable is used, which is at the point of connection. So it's evaluated in the context of a host which has no set_fact-derived variables defined: 'ec2.instances.0.network_interfaces.0.private_ip_address'. Only your play hosts will have set_fact-derived variables.

Assuming your hosts line is something like "hosts: localhost", then you can work around it by changing your variable definition to
ansible_ssh_user: "{{ amis[hostvars['localhost'].aws_region][hostvars['localhost'].os_version].user }}"

I'm reasonably sure (but could be wrong) that the "amis" dict is defined, because it isn't a host variable. That's different from set_fact-derived variables which are both host-specific and template resolved at their creation.

Let us know if this resolves your issue.
--
Todd

Evan Hisey

unread,
Jan 13, 2024, 12:25:36 PM1/13/24
to ansible...@googlegroups.com
Ricardo-
 I would also recommend changing how you are sending the json to the playbook. I have done this a lot in AWS and found it is much cleaner to either A) use a variable group file, or B) pass it via a parameter file. Both methods help avoid some of what Todd is referring to.

--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/679a3397-c893-4b88-be21-7faa3caeb2fen%40googlegroups.com.

ricardo barbosa

unread,
Jan 15, 2024, 5:00:31 PM1/15/24
to Ansible Project
Thanks Todd,

It worked. So because "delegate_to" the localhost host variables are not available I need to pass the variables with hostvars[ host ], right?

Thank you I will correct the entire playbook

ricardo barbosa

unread,
Jan 15, 2024, 5:01:57 PM1/15/24
to ansible...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages