Following on from the post about using Ansible to manage the 100+ Pingdom probe IP addresses, today’s post is very similar, but this time working with the IP addresses for the Palo Alto Global Protect Cloud Service (GPCS). To give the background and reasoning for this, when you use GPCS, you get a bunch of VMs around the globe that you could end up connected to when using the GlobalProtect VPN.
If you need to get a list of these then you can do an API call, after generating an API key in the Panorama server and doing a “curl” on a web address. These addresses can change, so it’s important to keep them updated. You can read more about it on the Palo Alto website.
The ansible script from yesterday gave me an idea about how I could use it for this as well. This one is a bit harder as we need to pass and uses variables, in this case, the API key.
Here it is.
UpdateGPCSIPs.yml:
ansible@ansible ~$ cat UpdateGPCSIPs.yml - hosts: Cisco_ASAs connection: local tasks: - name: GET CREDS include_vars: secrets.yml - name: DEFINE CONNECTION set_fact: connection: authorize: yes host: "{{ inventory_hostname }}" username: "{{ creds['username'] }}" password: "{{ creds['password'] }}" auth_pass: "{{ creds['auth_pass'] }}" timeout: 30 - shell: "curl -k -H header-api-key:{{api_key}} \"https://api.gpcloudservice.com/getAddrList/latest?fwType=$fwType&addrType=$addrType\" > /tmp/GPCS-IPs.txt" - replace: path: /tmp/GPCS-IPs.txt regexp: '", "' replace: "\n" - replace: path: /tmp/GPCS-IPs.txt regexp: '\[\"' replace: '' - replace: path: /tmp/GPCS-IPs.txt regexp: '\"\]' replace: "\n" - replace: path: /tmp/GPCS-IPs.txt regexp: 'total-count\"\: 44, \"addrList\"\: ' replace: '' - lineinfile: dest: /tmp/GPCS-IPs.txt regexp: '\{\"status\"\: \"success' state: absent - lineinfile: dest: /tmp/GPCS-IPs.txt regexp: 'result\"\: \{\"fwType\"\: \"all' state: absent - lineinfile: dest: /tmp/GPCS-IPs.txt regexp: 'addrListType\"\: \"all' state: absent - lineinfile: dest: /tmp/GPCS-IPs.txt regexp: '^DC03|^DC04|^DC02|^DC01|10.109|Dev-AWS|APAC' state: absent - lineinfile: path: /tmp/GPCS-IPs.txt regexp: '\}\}' state: absent - shell: cat /tmp/GPCS-IPs.txt | awk 'BEGIN { FS=":" } {print $2}' > /tmp/GPCS-IPs-only.txt register: IPs - asa_config: lines: - network-object host {{ item }} parents: ['object-group network GPCS-IPs'] provider: "{{ connection }}" with_lines: cat /tmp/GPCS-IPs-only.txt register: result - debug: var=result - asa_command: commands: - wr mem provider: "{{ connection }}"
A lot of will be familiar if you have read the previous post. Below I will explain the pertinent changes.
- shell: "curl -k -H header-api-key:{{api_key}} \"https://api.gpcloudservice.com/getAddrList/latest?fwType=$fwType&addrType=$addrType\" > /tmp/GPCS-IPs.txt"
This calls the web service, using the api_key variable, which is passed in the curly braces: {{api_key}}. We send the output to a file called GPCS-IPs.txt in the /tmp directory.
- replace: path: /tmp/GPCS-IPs.txt regexp: 'total-count\"\: 44, \"addrList\"\: ' replace: '' - lineinfile: dest: /tmp/GPCS-IPs.txt regexp: '\{\"status\"\: \"success' state: absent - lineinfile: dest: /tmp/GPCS-IPs.txt regexp: 'result\"\: \{\"fwType\"\: \"all' state: absent - lineinfile: dest: /tmp/GPCS-IPs.txt regexp: 'addrListType\"\: \"all' state: absent
We then have a bunch of replaces and lineinfile commands. This tidies up the output so that it is formatted as a nice list. The lineinfile command is useful when you want to remove a line, as the replace command will leave you with white lines. Using the state of “absent”, it removes the line, or lines, matched by the regex.
Because the output includes a lot of IP addresses that are local to your environment, as well as any remote networks VPN endpoints, these also need to be stripped out, so that you end up with just the external IP addresses within the AWS network:
- lineinfile: dest: /tmp/GPCS-IPs.txt regexp: '^DC03|^DC04|^DC02|^DC01|10.109|Dev-AWS|APAC' state: absent - lineinfile: path: /tmp/GPCS-IPs.txt regexp: '\}\}' state: absent
What we get at the end of the replaces and lineinfile statements is a list of locations and IP addresses, separated by a colon (:). To get just the IP addresses, we need to revert to some good old cat’ing and awk’ing:
- shell: cat /tmp/GPCS-IPs.txt | awk 'BEGIN { FS=":" } {print $2}' > /tmp/GPCS-IPs-only.txt register: IPs
I probably don’t need to register a result here, but what the hell.
The last section of the playbook, is to create the access-group along with the IP addresses we now have:
- asa_config: lines: - network-object host {{ item }} parents: ['object-group network GPCS-IPs'] provider: "{{ connection }}" with_lines: cat /tmp/GPCS-IPs-only.txt register: result - debug: var=result - asa_command: commands: - wr mem provider: "{{ connection }}"
We call the script using the command:
ansible-playbook UpdateGPCSIPs.yml -i hosts -e api_key=myapikeywhichisaweirdlookingstring
The “-e” is the same as using –extra-vars. We can tag as many variables as we want to, just remember to enclose multiple variables in brackets (-e “var1=hellow var2=world”).
So far, Ansible is proving a lot of fun. However, the scripts, as they stand so far would get me a big slap on the wrist from our PCI auditor, so we’ll fix that in the next post, and then after that, we’ll start creating some ACLs using the new access-groups that have been created!