Ansible Playbook for Cisco ASAv Firewall Topology

More about Ansible network automation with Cisco ASAv and continuous integration testing like in my previous posts using Vagrant and Gitlab-CI.

Network overview:

Here’s my Github repository where you can find the complete Ansible Playbook:

Automating firewall configuration is not that easy and can get very complicated because you have different objects, access-lists and service policies to configure which all together makes the playbook complex rather than simple.

What you won’t find in my playbook is how to automate the cluster deployment because this wasn’t possible in my scenario using ASAv and Vagrant. I didn’t have physical Cisco ASA firewall on hand to do this but I might add this later in the coming months.

Let’s look at the different variable files I created; first the host_vars for asa-1.yml which is very similar to a Cisco router:


hostname: asa-1
domain_name: lab.local

    alias: connection rtr-1 inside
    nameif: inside
    security_level: 100

    alias: connection rtr-2 outside
    nameif: outside
    security_level: 0

  - route outside 1

I then use multiple files in group_vars for objects.ymlobject-groups.ymlaccess-lists.yml and nat.yml to configure specific firewall settings.


  • Hostname: The task in main.yml uses the Ansible module asa_config and configures hostname and domain name.
  • Interfaces:  This role uses the Ansible module asa_config to deploy the template interfaces.j2 to configure the interfaces. In the main.yml is a second task to enable the interfaces when the previous template applied the configuration.
  • Routing: Similar to the interfaces role and uses also the asa_config module to deploy the template routing.j2 for the static routes
  • Objects: The first task in main.yml loads the objects.yml from group_vars, the second task deploys the template objects.j2.
  • Object-Groups: Uses same tasks in main.yml and template object-groups.j2 like the objects role but the commands are slightly different.
  • Access-Lists: One of the more complicated roles I needed to work on, in the main.yml are multiple tasks to load variables like in the previous roles, then runs a task to clear access-lists if the variable “override_acl” from access-lists.yml group_vars is set to “true” otherwise it skips the next tasks. When the variable are set to true and the access-lists are cleared it then writes new access-lists using the Ansible module asa_acl and finishes with a task to assigning the newly created access-lists to the interfaces.
  • NAT: This role is again similar to the objects role using a task main.yml to load variable file and deploys the template nat.j2. The NAT role uses object nat and only works if you created the object before in the objects group_vars.
  • Policy-Framework: Multiple tasks in main.yml first clears global policy and policy maps and afterwards recreates them. Similar approach like the access lists to keep it consistent.

Main Ansible Playbook site.yml


- hosts: asa-1

  connection: local
  user: vagrant
  gather_facts: 'no'

    - hostname
    - interfaces
    - routing
    - objects
    - object-groups
    - access-lists
    - nat
    - policy-framework

When a change triggers the gitlab-ci pipeline it spins up the Vagrant instances and executes the main Ansible Playbook. After the Vagrant instances are booted, first the two router rtr-1 and rtr-2 need to be configured with cisco_router_config.yml, then afterwards the main site.yml will be run.

Once the main playbook finishes for the Cisco ASA a last connectivity check will be execute using the playbook asa_check_icmp.yml. Just a simple ping to see if the base configuration is applied correctly.

If everything goes well, like in this example, the job is successful:

I will continue to improve the Playbook and the CICD pipeline so come back later to check it out.

Leave a comment

13 Replies to “Ansible Playbook for Cisco ASAv Firewall Topology”

  1. Hey,

    I’m wondering about the approach of clearing the ACLs when that task is run.

    What if I want to add to an existing ACL in production? Wouldn’t clearing it and then adding it again create a few seconds where the ACL didn’t exist and exiting traffic would stop?

    1. Hi Cathal,

      Yes, clearing the ACL when this is in production, is not a good idea and only works for new deployments. I have tried many things to get this to work without clearing the ACL but no success. I always ended up messing up the order of the ACL but the Ansible asa_acl module supports replacing lines. Maybe pushing the configuration with “with_subelements” is the problem but I have not tried this with the latest Ansible version.

      Give it a try and tell me if you get this to work?


  2. I really appreciate your kind instruction. It’s my first time to manage ASA with ansible and I don’t know how to make a host file. Usually I used to handle host file like ‘ [ios : vars] ‘.. but it doesn’t work with ASA.

    1. I guess you mean the inventory file, which is very simple, see my example below:

      asa-1 ansible_ssh_host= ansible_ssh_port=22 ansible_ssh_user='vagrant' ansible_ssh_private_key_file='/home/username/.vagrant.d/insecure_private_key'

      The inventory_hostname is asa-1 and the rest are just additional variables I need for this node to connect. You don’t need these additional variables in the inventory file, this could be also a simple dns name:

      Have fun playing around with ansible


  3. Hi,
    I’ve been trying to update an object-group using a template with no success. If I use the template to create a new object everything works fine but if the template uses an existing object it doesn’t work.
    Have you seen this in? Do you know a way around it?

    I really appreciate your help. Thanks!

    1. Hello Berna,
      Yes, this is the correct behaviour, the template is great for creating objects once but updating the same object name again is not possible.

      It should work with the asa_config module using lines and option “replace: line” but from my experience in earlier Ansible versions (2.4.x) this wasn’t working there..

      Here is an example I have used for class maps:

      - name: write class maps
          parents: ['class-map {{ }}']
            - "match {{ item.match }}"
          - "{{ class_map }}"

      Give it a try with using lines and let me know if this works in 2.6.x.


  4. I’m interested in automating ASAv right after the VM itself is deployed. I’ve found that when you log into the VM Console after deployment, you must:
    – enable
    – password (set a password)
    – password (confirm above password)
    – conf t
    – N (say no to participating in feedback, or Y)

    These steps are needed in order that my Ansible playbook can enter privileged mode so that I can populate the rest of the configuration using Ansible Module “asa_config”. However, I seem unable to automate the above steps. I’ve tried using the asa_config “before” and “lines” methods and the commands pass but then when I get to another step, it says “unable to enter privileged mode” meaning even though the commands seemed to pass through Ansible, they did not get populated in the ASA configuration properly. I’m wondering how you got around this or if you took a different method altogether of using templates or a day0.iso. I’m willing to entertain ANY method which will allow me to fully automate the deployment of ASAv (including the settings which must be specified when deploying the .OVA/.OVF. I’m thinking day0.iso will be the best way for me to go on this. Thoughts?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.