Skip to content
Feb 13 / Greg

Why Am I Pop Haydn

Hey everybody, I’m Greg Sowell and this is Why Am I, a podcast where I talk to interesting people and try to trace a path to where they find themselves today.  My guest this go around is Pop Haydn.  He grew up in rural Tennessee as a minister’s son, and in fact he almost become one himself until he thought: “Nah, I’ll become a magician instead” LOL.  He’s been all over, met his heroes, been a big part of the magic castle, and by all accounts been very accomplished, and yet even at 72 he’s still learning new things, still pushing himself.  Pop’s audio is a little rough here and there, but his stories well make up for it.  I hope you enjoy this chat with Pop.
Youtube version here:
If you want to support the podcast you can do so via https://www.patreon.com/whyamipod (this gives you access to bonus content including their Fantasy Restaurant!)
Feb 13 / Greg

The Brothers WISP 154 – Traveling Man, Zone Stuff, MTK New BGP Performance

This week we have Greg and Mike do the last duo cast for a while; Mike is about to join the elite club of fatherhood.

**Sponsors**
Sonar.Software
Towercoverage.com
**/Sponsors**

This week we talk about:
Doom on a 2011
map lite is perfect travel router…just wish it had 5ghz
What’s going on with Ansible AAP2.1?
Zhone Stuff
Ping Infrastructure
Sonar Communication
CCR2116 BGP Performance vs Juniper MX and Cisco ASR9k

Here’s the video:(if you don’t see it, hit refresh)

Feb 6 / Greg

Why Am I Jenny Radcliffe

Hey everybody, I’m Greg Sowell and this is Why Am I, a podcast where I talk to interesting people and try to trace a path to where they find themselves today. My guest this go around is Jenny Radcliff. I don’t know if I have the time to properly introduce her, but here’s some of the highlights: she’s a self proclaimed Scouser, an amazing story teller, an award winning podcast host, oh and by the way she’s paid by folks to burglarize them. This is often referred to as a social engineer, but in essence she’s paid to travel all over the world to lie, cheat, and steal her way into places to assess their physical security. She’s a serious badass, but she’s also empathetic and kind. I hope you enjoy this conversation with Jenny.
Youtube version here:
If you want to support the podcast you can do so via https://www.patreon.com/whyamipod (this gives you access to bonus content including their Fantasy Restaurant!)
Jan 30 / Greg

Why Am I Rocky Powell

Hey everybody, I’m Greg Sowell and this is Why Am I, a podcast where I talk to interesting people and try to trace a path to where they find themselves today.  My guest this go around is Rocky Powell.  This party god, and yes I’m calling her that, is a talented improviser in NYC, but she is also the host of the exceptionally funny Wild Nights podcast(https://linktr.ee/wildnightswithrockypowell) where she interviews comedians about some of their, you guessed it, wildest nights.  Join the rest of the party god squad and enjoy this chat with Rocky.
Youtube version here:
If you want to support the podcast you can do so via https://www.patreon.com/whyamipod (this gives you access to bonus content including their Fantasy Restaurant!)
Jan 30 / Greg

The Brothers WISP 153 – Mtk ROSv7.2, ISP Nutrition Labels, Ubiquiti AF 60XR

This week we have Greg, Nick A., Tommy C., Tom Smyth, Andrew Cox, and Mike Hammett! This is our 10 year anniversary cast, and we have some of the oldest faces as well as the new. I’m feeling a little under the weather, so thanks for everyone self moderating…right Tom?

**Sponsors**
Sonar.Software
Towercoverage.com
**/Sponsors**

This week we talk about:
New MTK ROSv7.2RC2-3
New Mikrotik device being hinted at TikTok Video… Lame!!!
Mikrotik’s label changing?
Mikrotik Discord Server sounds like it could be a thing
Update your Linux boxes (Again)
Nutrition Labels for your Internet Service
Ubiquiti WiFiman – UDM
Ubiquiti airFiber 60 XR

Here’s the video:(if you don’t see it, hit refresh)

Jan 25 / Greg

Automating The Creation And Installation Of Execution Environments For The Ansible Automation Platform

First things first, here’s my blog post that gives you a step by step of creating, preparing, and using custom Execution Environments. This article picks up from there and tries to automate as much of the process as is possible using the Ansible Automation Platform(AAP). It’s a little meta; I’m using AAP to build the Execution Environments(EEs) that are going to be used by AAP.

Video Demo

Alas, I’ve made adjustments to the playbook already, so there are slight differences.

Playbook

The link to the newest updated version of the playbook is right here in my github.
This is the complete playbook at the time of this writing(again, check github for the newest version). Below will be the playbook broken into pieces and explained.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
---
- name: Build/Push/Install Custom EEs
  hosts: aap.gregsowell.com
  gather_facts: false
  vars:
    # aap username/pass
    controller_user: "{{ gen2_user }}"
    controller_pass: "{{ gen2_pword }}"
 
#    # what is the base EE that is used for building my custom EE
#    base_ee: registry.redhat.io/ansible-automation-platform-21/ee-supported-rhel8
 
    # what is the name of the new EE being built
    new_ee: azure_ee_supported
 
    # what is the path where the EE build files are stored
    path_ee: /root/ansible-execution-environments
    # base folder for cloning EE
    base_ee_path: /root
    # private automation hub host
    pah_host: pah.gregsowell.com
    # ee repo
    ee_repo: https://github.com/gregsowell/ansible-execution-environments
 
    # private automation hub host
    pah_host: pah.gregsowell.com
 
    # credential name used in AAP
    pah_cred: Automation Hub Container Registry
 
#    # new version number for the pushed ee
#    new_ee_ver: 1.0.2
 
    # pah username/pass
    pah_user: "{{ gen1_user }}"
    pah_pass: "{{ gen1_pword }}"

I setup a username and password for connecting via the AAP API; I’m passing in custom credentials at run-time(a benefit of using custom credentials is that if I specify a field as password, then no_log is set for that variable and will be obfuscated in output).

You may notice that I have base_ee commented out. This is the base container I’m using to build the EE with. I’ve got it commented out because I’m pulling that directly from the EE configuration files, so it’s not necessary here(I left it if you want to change things up.

There’s also setup for the build files repo; I’ve set it up so that all of my kit is stored in Source Control Management!

You’ll also notice that I’m setting up info for using Red Hat’s Private Automation Hub(PAH or pah). I’m using this as my container registry and will be pushing my custom EEs here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  tasks:
  - name: Git clone the EE repo to ensure I have the newest files
    ansible.builtin.shell: "git clone {{ ee_repo }}"
    args:
      chdir: "{{ base_ee_path }}"
 
  - name: Grab the execution-environment.yml file to use as variables
    ansible.builtin.fetch:
      src: "{{ path_ee }}/{{ new_ee }}/execution-environment.yml"
      dest: execution-environment.yml
    register: ee_yml
 
  - name: Include the EE file as variables
    include_vars:
      file: "{{ ee_yml.dest }}"
      name: inc_vars
 
  - name: Setup some base variables
    ansible.builtin.set_fact:
      base_ee: "{{ inc_vars.build_arg_defaults.EE_BASE_IMAGE }}"
      new_ee_ver: "{{ inc_vars.version }}"

This beginning is all about reading the EE config files and grabbing some of the info. First things first, I connect to my git repo and clone down the newst build files. Next reach out to the remote server I’m using to build EEs at and pulling that file locally into my current EE for processing. Notice I’m registering the results to a variable; this is because the path that containers use are pretty bonkers, and it’s easier to just reference it this way. For this example, the path is “/runner/project/execution-environment.yml/aap.gregsowell.com/root/ee/azure_ee_supported/execution-environment.yml”.

I next include the execution-environment.yml file as variables(it is a simply YAML formatted file after all).

Last here I grab the interesting variables(base image and version) and assign them.

1
2
3
4
5
6
7
8
9
  - name: Grab the list of EEs to ensure the correct EE exists
    ansible.builtin.shell: podman image list
    changed_when: false
    register: ee_list
 
  - name: Install the required base EE if not already present
    when: ee_list.stdout_lines is not search(base_ee)
    ansible.builtin.debug:
      msg: "Install the missing base EE here"

Here I’m issuing a “podman image list” command to view what EEs exist on this server and registering it to a variable. Next I do a quick check to see if that base EE I’m building from exists on the server; if not it will pull this EE(I’ll be adding that in version 2 of the script).

1
2
3
4
5
6
7
8
9
  - name: Begin the build process on the EE
    when: ee_list.stdout_lines is search(base_ee)
    block:
      - name: Run the build on the selected new EE
        ansible.builtin.shell: "ansible-builder build --tag {{ new_ee }}"
        args:
          chdir: "{{ path_ee }}/{{new_ee}}"
        register: ee_build_out
        failed_when: ee_build_out.stdout_lines is not search('Complete!')

On my AAP server I have a directory named “ee”. Inside of it I keep a directory for each custom EE I’m building. So for example I have /root/ee/azure_ee_supported/. This is where I’m building, you guessed it, my Azure execution environment. Inside of this folder is all of my ansible-builder files(and ultimately where the context folder will be placed by builder). I’m using the shell module here to fire off the build command, but I have to first add the args option to specify which folder to execute the command in. I’m also adding in a failed_when condition so that if the command doesn’t return “Complete!”, it will fail the task. This is the standard build.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  - name: search containing folder to determine if there are any crt files(used for self-signed pah server)
    ansible.builtin.find:
      paths: "{{ path_ee }}/{{ new_ee }}"
      patterns: '*.crt'
    register: crt_files
 
  - name: Begin the build process on the EE when certs do need to be installed
    when: ee_list.stdout_lines is search(base_ee) and crt_files.matched > 0
#    when: ee_list.stdout_lines is search(inc_vars.build_arg_defaults.EE_BASE_IMAGE)
    block:
      - name: Run the create on the selected new EE - **install certs process**
        ansible.builtin.shell: "ansible-builder create"
        args:
          chdir: "{{ path_ee }}/{{new_ee}}"
        register: ee_build_out
        failed_when: ee_build_out.stdout_lines is not search('Complete!')
 
      - name: Copy cert files to the build folder
        ansible.builtin.shell: "cp {{ path_ee }}/{{new_ee}}/*.crt {{ path_ee }}/{{new_ee}}/context/_build"
        args:
          chdir: "{{ path_ee }}/{{new_ee}}"
 
      - name: Add line to Containerfile to ensure the certificates are copied and applied
        ansible.builtin.lineinfile:
          path: "{{ path_ee }}/{{new_ee}}/context/Containerfile"
          insertbefore: '^RUN ansible-galaxy role install.*'
          line: 'RUN cp *.crt /etc/pki/ca-trust/source/anchors && update-ca-trust'
 
      - name: Run the build on the selected new EE - with installed certs
        ansible.builtin.shell: "podman build -f context/Containerfile -t {{ new_ee }} context"
        args:
          chdir: "{{ path_ee }}/{{new_ee}}"
        register: ee_build_out

^^The above section is run when certificates need to be installed to connect to my private automation hub.
The first task checks the EE’s folder to see if there are any certificate files.
I then have a block with a conditional checking to see if there were any cert files, and when it found some the block will run.
I first run the ansible-builder create command which will build out the Containerfile which will be used to create the EE.
I then copy my certs to the _build folder, and then modify the Container file to install the certs.
Last I run the podman build command to create my new EE.
I have a breakdown of what’s happening in the blog post on all of the steps being performed here.

1
2
3
4
5
6
  - name: Push EE to container registry
    ansible.builtin.shell: "podman push --creds={{ pah_user }}:{{ pah_pass }} localhost/{{ new_ee }}:latest docker://{{ pah_host }}/{{ new_ee }}:{{ new_ee_ver }} --tls-verify=false"
    args:
      chdir: "{{ path_ee }}/{{new_ee}}"
    register: pah_push_out
    failed_when: pah_push_out.stderr_lines is not search('Storing signatures')

I’m next using the podman command to push the newly created EE to my PAH server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  - name: Collect credentials
    uri:
      url: "https://{{ inventory_hostname }}/api/v2/credentials/"
      user: "{{ controller_user }}"
      password: "{{ controller_pass }}"
      method: GET
      validate_certs: false
      force_basic_auth: true
      status_code:
        - 200
        - 201
        - 204
    register: aap_cred_out
 
  - name: Set ansible credential name to ID
    when: item.name == pah_cred
    ansible.builtin.set_fact:
      pah_cred_id: "{{ item.id }}"
    loop: "{{ aap_cred_out.json.results }}"

Alright, the first thing I need to do is map the credential name over to the credential ID. I do this by first pulling all of the existing creds and registering them to a variable(aap_cred_out).
I next loop through aap_cred_out looking for the credential name specified in the vars section(pah_cred), then once it finds the cred name in question, it will assign it’s ID to a variable(pah_cred_id).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  - name: Collect EEs to check if it already exists
    uri:
      url: "https://{{ inventory_hostname }}/api/v2/execution_environments/"
      user: "{{ controller_user }}"
      password: "{{ controller_pass }}"
      method: GET
      validate_certs: false
      force_basic_auth: true
      status_code:
        - 200
        - 201
        - 204
    changed_when: false
    register: aap_ee_out
 
  - name: Set ansible ee id if name already exists
    when: item.name == new_ee
    ansible.builtin.set_fact:
      new_ee_id: "{{ item.id }}"
    loop: "{{ aap_ee_out.json.results }}"

Here I’m now going to collect a list of EEs. I’m then looping through them to see if the EE I’m trying to add already exists or not(just in name, not in version).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  - name: Configure AAP for new EE
    when: new_ee_id is undefined
    vars:
      hosts: controllers
    uri:
      url: "https://{{ inventory_hostname }}/api/v2/execution_environments/"
      user: "{{ controller_user }}"
      password: "{{ controller_pass }}"
      method: POST
      validate_certs: false
      force_basic_auth: true
      body_format: json
      body: |
          {
              "name": "{{ new_ee }}",
              "image": "{{ pah_host }}/{{ new_ee }}:{{ new_ee_ver }}",
              "description": "added by automation",
              "pull": "missing",
              "credential": "{{ pah_cred_id }}"
          }
      status_code:
        - 200
        - 201
        - 204
    failed_when: aap_conf_out.status != 201 and aap_conf_out.json.name is not search('already exists')
    register: aap_conf_out
 
#  - name: Debug aap_conf_out
#    ansible.builtin.debug:
#      var: aap_conf_out
 
  - name: If EE already exists on AAP, update version number
#    when: aap_conf_out.status == 400 and aap_conf_out.json.name is search('already exists')
    when: new_ee_id is defined
    vars:
      hosts: controllers
    uri:
      url: "https://{{ inventory_hostname }}/api/v2/execution_environments/{{ new_ee_id }}/"
      user: "{{ controller_user }}"
      password: "{{ controller_pass }}"
      method: PUT
      validate_certs: false
      force_basic_auth: true
      body_format: json
      body: |
          {
              "name": "{{ new_ee }}",
              "image": "{{ pah_host }}/{{ new_ee }}:{{ new_ee_ver }}",
              "description": "added by automation",
              "pull": "missing",
              "credential": "{{ pah_cred_id }}"
          }
      status_code:
        - 200
        - 201
        - 204
#    failed_when: aap_conf_out.status != 201 and aap_conf_out.json.name is not search('already exists')
    register: aap_conf_patch_out

Now I a add the new EE into my controllers.
First I check if the EE exists, and if not, I simply add it.
If the EE does exist, then I do an update to the existing settings. This way if a new version of the EE has been created, the version number will be updated in the controller.

Just as a note I like to set my EEs to pull if missing. This way it will only pull the EE the one time it needs to(much more efficient). Also note that if I do an update on the EE version number in my PAH and also update the EE version number in my controller, on the next piece of automation that runs using that EE, it will check and see that the stored EE has an older version, so it will connect to the PAH and download the new version. So in short, updating the EE version number in controller will signal that it should pull the new EE from my container registry!

Conclusion

At this point the automation is done and I’m ready to start using the EE with my job templates. This cuts the majority of manual labor out of the process, and makes my life just that much easier.

Again, this is version 1, so I plan to make it more efficient, so watch for updates.

If you have any questions or comments, please fire them my way, and happy automating.

Jan 23 / Greg

Fantasy Restaurant Jeni B

Welcome to the warmup exercise for the Why Am I podcast called “the Fantasy Restaurant.”  In here my guests get to pick their favorite: drink, appetizer, main, sides, and dessert.  There are no rules in the restaurant…not even the laws of physics exist if you don’t want them to.  These are usually a patron only exclusive, but I’m putting this out there because Jeni is honestly just too much fun, and she puts together a hell of a meal.  To hear more, head to https://www.patreon.com/whyamipod and I hope you enjoy this meal with Jeni.  
Youtube version here:
If you want to support the podcast you can do so via https://www.patreon.com/whyamipod (this gives you access to bonus content including their Fantasy Restaurant!)