First ansible use – install and execute a single command or multiple tasks in a playbook

This article is to show you how easy is to use automation tools for managing your servers. If you are new to ansible this article is right for you!

Installation

First, you must install ansible, which is pretty easy. At present all Linux distributions have the ansible package:

Ubuntu

sudo apt install ansible

CentOS 7

sudo yum install ansible

Fedora

sudo dnf install ansible

Gentoo

emerge -v ansible

Multiple python (version 3) packages will be pulled because the tool is based on python. The following files will appear in your machine (and a lot of python modules under the python directory of your Linux distribution):

/usr/bin/ansible
/usr/bin/ansible-config -> ansible
/usr/bin/ansible-connection
/usr/bin/ansible-console -> ansible
/usr/bin/ansible-doc -> ansible
/usr/bin/ansible-galaxy -> ansible
/usr/bin/ansible-inventory -> ansible
/usr/bin/ansible-playbook -> ansible
/usr/bin/ansible-pull -> ansible
/usr/bin/ansible-vault -> ansible

The important program name is ansible, with which you can do any of the other task.

What you can do using ansible with simple words

At present (July 2019) ansible 2.8.x has around 2080 modules (all modules here https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html) so you will find a solution for any automation task you may encounter. But here our purpose is to show you several simple commands.

ansible uses ssh to connect remotely to other machines and it is the best option to use ssh keys for passwordless connections

Still, ansible has the option to use also password authentication with “–ask-pass” option. In fact, connecting to the remote host could be done without ssh, but another protocol and this is beyond the scope of this article and it is rarely used.

Ansible modules could be used with different Linux distributions without specifying what kind of packaging software or init system is used.

So when you use module to install a package in your server you may not specify to use apt, yum or any other, or when you want to stop/start/reload/restart a service you do not need to specify it is a systemd or openrc or upstart or sysvinit and so on. The modules gather this information from the currently connected remote host and use the proper command to do its job. Look below in the playbook section.

The inventory file

The first thing to do is your file with servers. In terms of ansible, this is your “inventory file” – the file describing how to connect to your servers like hostname, ports, keys and so on.
The default inventory file is in /etc/ansible/hosts, but you can use file in any location if you include it in the ansible with “-i
So open your favorite text editor and write down your servers (it supports two syntaxes INI and YAML styles):

1) Just enumerate your servers’ hostnames.

Using default port 22 and the user you are logged in. Still, if you use “~/.ssh/config” and you included specific options like port, user, identity file these options will be used by ansible to connect to the hosts.

srv1.example.com
srv2.example.com

2) Or you can include options.

Like port, user, private key

srv1 ansible_host=10.10.10.10 ansible_port=10077 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_private_key_file=~/.ssh/example-ident
srv2 ansible_host=srv2.example.com ansible_port=10077 ansible_connection=ssh ansible_ssh_user=myuser ansible_ssh_private_key_file=~/.ssh/example-ident

The host name could be different from the ansible_host, which will be used to connect to. Here we change the default behavior – port=10077, use the root user to connect to the remote host with the private key from the current user directory. So if you are logged with let’s say “myuser” you are going to connect to the host with your private key and user root. The configuration in the inventory file overwrites the configuration of your openssh (“~/.ssh/config”).

3) Servers’ groups in the inventory file

[storages]
srv1 ansible_host=10.10.10.10 ansible_port=10077 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_private_key_file=~/.ssh/example-ident
srv2 ansible_host=srv2.example.com ansible_port=10077 ansible_connection=ssh ansible_ssh_user=myuser ansible_ssh_private_key_file=~/.ssh/example-ident

[webservers]
srv3 ansible_host=srv1.example.com ansible_port=10077 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_private_key_file=~/.ssh/example-ident

[project1:children]
storages
webservers

We have two groups consisting of server names and a group, which includes the two groups “storages” and “webservers”. You could use the group names in the ansible command to execute commands.

List all hosts in the inventory file

myuser@srv ~ $ ansible -i ./inventory.ini --list-hosts all
  hosts (3):
    srv1
    srv2
    srv3

Check if everything works properly and ansible could connect the hosts (on all remote hosts you must have python installed to use ansible, preferable to be python 3+ verions). Use ping module:

myuser@srv ~ $ ansible all -i ./inventory.ini -m ping
srv2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
srv3 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
srv1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

4) Local execution

In some cases you may want to execute local tasks (i.e. commands) so here is what to include in the inventory file:

localhost ansible_connection=local

When you execute multiple tasks (i.e. commands) in a playbook (i.e. a file with multiple commands) you may want to use ansible locally in a first boot after installation.

More on the subject of inventory file here https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html

Connecting to the remote servers and executing simple command(s)

Here we load the inventory file and use module shell to execute a command on the remote server srv1:

myuser@srv ~ $ ansible srv1 -i ./inventory.ini -m shell -a 'uname -a'
srv1 | CHANGED | rc=0 >>
Linux srv1.example.com 3.10.0-862.3.2.el7.x86_64 #1 SMP Mon May 21 23:36:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Again, if you use /etc/ansible/hosts you could skip “-i ” (“./inventory.ini”)

Simple multiple server connect (inventory file) and hosts’ groups

“all” is the host pattern for all servers in the inventory file (or you can use host names from the inventory file like “srv1” in our example above)

myuser@srv ~ $ ansible webservers -i ./inventory.ini -m shell -a 'uname -a'

srv3 | CHANGED | rc=0 >>
Linux srv3.example.com 4.18.12 #2 SMP Tue Oct 9 20:48:52 UTC 2018 x86_64 Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz GenuineIntel GNU/Linux

Execute “whoami” to all hosts:

myuser@srv ~ $ ansible all -i ./inventory.ini -m shell -a 'whoami'
srv1 | CHANGED | rc=0 >>
root

srv2 | CHANGED | rc=0 >>
myuser

srv3 | CHANGED | rc=0 >>
root

As you can see we user “myuser” to log in in srv2 and the root for srv1 and srv3. srv1 uses sudo to escalate privileges to root for better security.

Executing commands using sudo to escalate to root

In your remote servers change “/etc/sudoers” to have a like like:

%wheel        ALL=(ALL)       NOPASSWD: ALL

and comment the line if it exits:

#%wheel  ALL=(ALL)       ALL

Then add the group to the user, with which you are going to connect to the remote server with:

usermod -a -G wheel myuser

And here we can use sudo (or various ansible ways to become another/root user):

myuser@srv ~ $ ansible all -i ./inventory.ini -m shell -a 'sudo whoami'
 [WARNING]: Consider using 'become', 'become_method', and 'become_user' rather than running sudo

srv1 | CHANGED | rc=0 >>
root

srv2 | CHANGED | rc=0 >>
root

srv3 | CHANGED | rc=0 >>
root

Of course, we suppose your distribution uses wheel group (most modern distributions have wheel group for administrative tasks by users). If you do not have it just replace it with the group name of the user you are going to connect to the server and replace “wheel” with the group name (or you can use the username for this purpose, too).
Copy a local file to all remote servers in /root directory using ansible module “copy”. Note we use “-b” to become the requested user if needed (as you may recall our connection to the “srv2” is using “myuser”):

myuser@srv ~ $ ansible all -i ./inventory.ini -m copy -a 'src=copy-test-file.txt dest=/root/ owner=root group=root mode=0644' -b
srv1 | CHANGED => {
    "changed": true,
    "checksum": "12039d6dd9a7e27622301e935b6eefc78846802e",
    "dest": "/root/copy-test-file.txt",
    "gid": 0,
    "group": "root",
    "md5sum": "7c12772809c1c0c3deda6103b10fdfa0",
    "mode": "0644",
    "owner": "root",
    "secontext": "system_u:object_r:admin_home_t:s0",
    "size": 11,
    "src": "/root/.ansible/tmp/ansible-tmp-1564998029.2082274-174670050907050/source",
    "state": "file",
    "uid": 0
}
srv2 | CHANGED => {
    "changed": true,
    "checksum": "12039d6dd9a7e27622301e935b6eefc78846802e",
    "dest": "/root/copy-test-file.txt",
    "gid": 0,
    "group": "root",
    "md5sum": "7c12772809c1c0c3deda6103b10fdfa0",
    "mode": "0644",
    "owner": "root",
    "secontext": "system_u:object_r:admin_home_t:s0",
    "size": 11,
    "src": "/home/myuser/.ansible/tmp/ansible-tmp-1564998029.1584368-84263920779880/source",
    "state": "file",
    "uid": 0
}
srv3 | CHANGED => {
    "changed": true,
    "checksum": "12039d6dd9a7e27622301e935b6eefc78846802e",
    "dest": "/root/copy-test-file.txt",
    "gid": 0,
    "group": "root",
    "md5sum": "7c12772809c1c0c3deda6103b10fdfa0",
    "mode": "0644",
    "owner": "root",
    "size": 11,
    "src": "/root/.ansible/tmp/ansible-tmp-1564998029.199494-96448253016479/source",
    "state": "file",
    "uid": 0
}

Ansible playbook and multiple commands (tasks)

In the last example above, when copying file from local using copy ansible module, you may want to have multiple commands before and/or after the copying and it is a way more convinient to describe the commands in a file and then just execute a simple command line. Let’s say you want to copy the new configuration files to your webserver and then reload the web server – no need to write long command lines (and then probably save them to some place) you can use

playbook files, which are with the simple words a list(s) of commands (ansible uses “tasks”, because it may have not been commands to execute)!

Ansible playbook consists of tasks to manage configurations and software deployment. The playbook uses YAML format to describe the tasks.
Here is a simple task to reload the nginx web server. Put the following in a text file

---
- hosts: all
  tasks:
      - name: Reload nginx
        service: name=nginx state=reloaded
        become: yes

Note: do not use tabs for spacing.
And then execute the playbook with:

myuser@srv ~ $ ansible-playbook -l srv2 -i ./inventory.ini ./playbook-example.yml

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [srv2]

TASK [Reload nginx] ********************************************************************************************************************************************************
changed: [srv2]

PLAY RECAP *****************************************************************************************************************************************************************
srv2                       : ok=2    changed=1    unreachable=0    failed=0   

And our nginx was reloaded successfully. This playbook uses “all” for hosts, but in the command line we specify only host “srv2” with the “-l” option.
Including the option “-v” can show you some debug information:

myuser@supermyuser ~ $ ansible-playbook -v -l srv2 -i ./inventory.ini ./playbook-example.yml -b
No config file found; using defaults
/home/myuser/inventory.ini did not meet host_list requirements, check plugin documentation if this is unexpected
/home/myuser/inventory.ini did not meet script requirements, check plugin documentation if this is unexpected
/home/myuser/inventory.ini did not meet yaml requirements, check plugin documentation if this is unexpected

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [srv2]

TASK [Reload nginx] ********************************************************************************************************************************************************
changed: [srv2] => {"changed": true, "name": "nginx", "state": "started", "status": {"ActiveEnterTimestamp": "Mon 2019-02-25 13:53:46 UTC", "ActiveEnterTimestampMonotonic": "5869633632962", "ActiveExitTimestamp": "Mon 2019-02-25 13:53:45 UTC", "ActiveExitTimestampMonotonic": "5869632613882", "ActiveState": "active", "After": "systemd-journald.socket network-online.target remote-fs.target nss-lookup.target basic.target system.slice", "AllowIsolate": "no", "AmbientCapabilities": "0", "AssertResult": "yes", "AssertTimestamp": "Mon 2019-02-25 13:53:45 UTC", "AssertTimestampMonotonic": "5869633381024", "Before": "multi-user.target shutdown.target", "BlockIOAccounting": "no", "BlockIOWeight": "18446744073709551615", "CPUAccounting": "no", "CPUQuotaPerSecUSec": "infinity", "CPUSchedulingPolicy": "0", "CPUSchedulingPriority": "0", "CPUSchedulingResetOnFork": "no", "CPUShares": "18446744073709551615", "CanIsolate": "no", "CanReload": "yes", "CanStart": "yes", "CanStop": "yes", "CapabilityBoundingSet": "18446744073709551615", "ConditionResult": "yes", "ConditionTimestamp": "Mon 2019-02-25 13:53:45 UTC", "ConditionTimestampMonotonic": "5869633381024", "Conflicts": "shutdown.target", "ControlGroup": "/system.slice/nginx.service", "ControlPID": "0", "DefaultDependencies": "yes", "Delegate": "no", "Description": "nginx - high performance web server", "DevicePolicy": "auto", "Documentation": "http://nginx.org/en/docs/", "ExecMainCode": "0", "ExecMainExitTimestampMonotonic": "0", "ExecMainPID": "31012", "ExecMainStartTimestamp": "Mon 2019-02-25 13:53:46 UTC", "ExecMainStartTimestampMonotonic": "5869633632910", "ExecMainStatus": "0", "ExecReload": "{ path=/bin/kill ; argv[]=/bin/kill -s HUP $MAINPID ; ignore_errors=no ; start_time=[Mon 2019-08-05 10:34:48 UTC] ; stop_time=[Mon 2019-08-05 10:34:48 UTC] ; pid=7688 ; code=exited ; status=0 }", "ExecStart": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx -c /etc/nginx/nginx.conf ; ignore_errors=no ; start_time=[Mon 2019-02-25 13:53:45 UTC] ; stop_time=[Mon 2019-02-25 13:53:46 UTC] ; pid=31007 ; code=exited ; status=0 }", "ExecStop": "{ path=/bin/kill ; argv[]=/bin/kill -s TERM $MAINPID ; ignore_errors=no ; start_time=[Mon 2019-02-25 13:53:45 UTC] ; stop_time=[Mon 2019-02-25 13:53:45 UTC] ; pid=30989 ; code=exited ; status=0 }", "FailureAction": "none", "FileDescriptorStoreMax": "0", "FragmentPath": "/usr/lib/systemd/system/nginx.service", "GuessMainPID": "yes", "IOScheduling": "0", "Id": "nginx.service", "IgnoreOnIsolate": "no", "IgnoreOnSnapshot": "no", "IgnoreSIGPIPE": "yes", "InactiveEnterTimestamp": "Mon 2019-02-25 13:53:45 UTC", "InactiveEnterTimestampMonotonic": "5869632664810", "InactiveExitTimestamp": "Mon 2019-02-25 13:53:45 UTC", "InactiveExitTimestampMonotonic": "5869633386655", "JobTimeoutAction": "none", "JobTimeoutUSec": "0", "KillMode": "control-group", "KillSignal": "15", "LimitAS": "18446744073709551615", "LimitCORE": "18446744073709551615", "LimitCPU": "18446744073709551615", "LimitDATA": "18446744073709551615", "LimitFSIZE": "18446744073709551615", "LimitLOCKS": "18446744073709551615", "LimitMEMLOCK": "65536", "LimitMSGQUEUE": "819200", "LimitNICE": "0", "LimitNOFILE": "4096", "LimitNPROC": "126979", "LimitRSS": "18446744073709551615", "LimitRTPRIO": "0", "LimitRTTIME": "18446744073709551615", "LimitSIGPENDING": "126979", "LimitSTACK": "18446744073709551615", "LoadState": "loaded", "MainPID": "31012", "MemoryAccounting": "no", "MemoryCurrent": "7798784000", "MemoryLimit": "18446744073709551615", "MountFlags": "0", "Names": "nginx.service", "NeedDaemonReload": "no", "Nice": "0", "NoNewPrivileges": "no", "NonBlocking": "no", "NotifyAccess": "none", "OOMScoreAdjust": "0", "OnFailureJobMode": "replace", "PIDFile": "/var/run/nginx.pid", "PermissionsStartOnly": "no", "PrivateDevices": "no", "PrivateNetwork": "no", "PrivateTmp": "no", "ProtectHome": "no", "ProtectSystem": "no", "RefuseManualStart": "no", "RefuseManualStop": "no", "RemainAfterExit": "no", "Requires": "basic.target", "Restart": "no", "RestartUSec": "100ms", "Result": "success", "RootDirectoryStartOnly": "no", "RuntimeDirectoryMode": "0755", "SameProcessGroup": "no", "SecureBits": "0", "SendSIGHUP": "no", "SendSIGKILL": "yes", "Slice": "system.slice", "StandardError": "inherit", "StandardInput": "null", "StandardOutput": "journal", "StartLimitAction": "none", "StartLimitBurst": "5", "StartLimitInterval": "10000000", "StartupBlockIOWeight": "18446744073709551615", "StartupCPUShares": "18446744073709551615", "StatusErrno": "0", "StopWhenUnneeded": "no", "SubState": "running", "SyslogLevelPrefix": "yes", "SyslogPriority": "30", "SystemCallErrorNumber": "0", "TTYReset": "no", "TTYVHangup": "no", "TTYVTDisallocate": "no", "TasksAccounting": "no", "TasksCurrent": "8", "TasksMax": "18446744073709551615", "TimeoutStartUSec": "1min 30s", "TimeoutStopUSec": "1min 30s", "TimerSlackNSec": "50000", "Transient": "no", "Type": "forking", "UMask": "0022", "UnitFilePreset": "disabled", "UnitFileState": "enabled", "WantedBy": "multi-user.target", "Wants": "network-online.target system.slice", "WatchdogTimestamp": "Mon 2019-02-25 13:53:46 UTC", "WatchdogTimestampMonotonic": "5869633632935", "WatchdogUSec": "0"}}

PLAY RECAP *****************************************************************************************************************************************************************
srv2                       : ok=2    changed=1    unreachable=0    failed=0 

The verbose output level could be raised by adding more “v”s like “-vvv”.
Here we are using the service ansible module you can check more here – https://docs.ansible.com/ansible/latest/modules/service_module.html

The playbook could include multiple tasks like the following:

---
- hosts: all
  tasks:
      - name: copy ssl certificates to nginx folder
        copy: src=/home/myuser/projects/web-example/ALL.example.com.crt dest=/etc/ssl/nginx/ALL.example.com.crt owner=root group=root mode=400 backup=yes
        tags: certificates
        
      - name: copy script file to storage folder
        copy: src=/home/myuser/projects/web-example/scripts/parse.php dest=/var/www/parse.php owner=root group=root mode=755 backup=no
        tags: script_logs

      - name: copy referer_blacklist to all servers
        copy: src=/home/myuser/projects/web-example/configs/referer_blacklist.conf dest=/etc/nginx/conf.d/referer_blacklist.conf owner=root group=root mode=644 backup=no
        tags: referer_blacklist
                
      - name: enable-cors
        copy: src=/home/myuser/projects/web-example/configs/enable-cors.conf dest=/etc/nginx/conf.d/enable-cors.conf owner=root group=root mode=644 backup=no
        tags: enable-cors

Multiple commands to copy configuration files. You can execute all of them with:

myuser@srv ~ $ ansible-playbook -l srv3 -i ./inventory.ini ./playbook-example-2.yml

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [srv3]

TASK [copy ssl certificates to nginx folder] *******************************************************************************************************************************
changed: [srv3]

TASK [copy script file to storage folder] **********************************************************************************************************************************
changed: [srv3]

TASK [copy referer_blacklist to all servers] *******************************************************************************************************************************
changed: [srv3]

TASK [enable-cors] *********************************************************************************************************************************************************
changed: [srv3]

PLAY RECAP *****************************************************************************************************************************************************************
srv3                       : ok=5    changed=4    unreachable=0    failed=0

As you can see we included in every task the keyword “tags”, “tags” means you can mark the task with a name and execute it (or them if you include a specific tag in multiple tasks). This can be done with:

ansible-playbook --tags certificates -l all -i ./inventory.ini ./playbook-example-2.yml

More on ansible playbook here – https://docs.ansible.com/ansible/latest/user_guide/playbooks.html

Protecting sensitive information with encryption – the Ansible Vault

You can protect basically every part of your ansible project – files, parts of a playbook, variables and so on. The password could be entered in the command line when executing the playbook or read from a file.
Here are some examples:

1) Create a new encrypted file with sensitive information

You can use this method to create and encrypt a file with variables, for example:

myuser@srv ~ $ ansible-vault create protected.yml
New Vault password: 
Confirm New Vault password:

And it will open your default editor (taken from the EDITOR environment). In our case, it was nano and then write your variables like:

---
certname1: "ALL.example.com"
pass1: "ainaikai%sheuze9quiebu+xau4ooK"
pass2: "toh9ti3wai&maem5yen9Ung8ip0Fie"
pass3: "Eid0AiY~ae6ais8aecei7oow#u8Ke7"

Here is the encrypted file:

$ANSIBLE_VAULT;1.1;AES256
37316234623436343266653364616339316237323634336633356362376238616136353361393337
3437363630316666626333326464656664396136633334300a333238633932366162656332376534
36326132643063303964316561646162626235386362353361396533353434613539626235623738
3635363834663266390a666566656365306635316536366666396233323966386132306134326562
39316235376362666536653135613231336330653832353438353138636462356232626130303237
30626531366138626531663363633035323138623633613236303363303533343062353334626336
64396262323164663266666232383230323966336262663464313064383039336462623462383062
64353239393430303430383838306564343030373962316631646666653761333765656437363634
30353362303332353539613830383738663532613835386533613766323564363234

Then in your playbook you can just include the file with:

---
- hosts: all
  vars_files:
    - 'protected.yml'
  tasks:
    - name: copy ssl certificates to nginx folder
      copy: src=/home/myuser/projects/web-example/{{ certname1 }}.crt dest=/etc/ssl/nginx/{{ certname1 }}.crt owner=root group=root mode=400 backup=yes
      tags: certificates

    - name: copy the password file of the certificate
      copy: content="{{ pass1 }}" dest=/etc/ssl/nginx/{{ certname1 }}.psw owner=root group=root mode=400 backup=yes
      tags: certificates

    - debug:
        msg: Cert - {{ certname1 }}
      tags: mydebug

So all variables in protected.yml can be used in your ansible playbook as ordinary variables. In the example above we use simple variables stored in a protected vault file for the name and the password of our certificate “ALL.example.com”. The playbook will copy the certificate and will create a file with the certificate’s password (with the proper permissions “root:root” and 400) to be used in nginx web server. You can also use templates with the vault variables to replace the certificate name and password, but this is beyond the scope of this article.

myuser@srv ~ $ ansible-playbook --ask-vault-pass -l srv3 -i ./inventory.ini ./playbook-example-2.yml
Vault password: 

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [srv3]

TASK [copy ssl certificates to nginx folder] *******************************************************************************************************************************
ok: [srv3]

TASK [copy the password file of the certificate] ***************************************************************************************************************************
changed: [srv3]

TASK [debug] ***************************************************************************************************************************************************************
ok: [srv3] => {
    "msg": "Cert - ALL.example.com"
}

PLAY RECAP *****************************************************************************************************************************************************************
srv3                       : ok=4    changed=1    unreachable=0    failed=0

2) You can create an encrypted variable in the ansible playbook file

First, encrypt the variable with”

myuser@srv ~ $ ansible-vault encrypt_string 'SoC6ahGheech@aengei3' --name 'the_pass_3'
New Vault password: 
Confirm New Vault password: 
the_pass_3: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          61663935383430323364373037386234346236323637343030303835373466663733613262636664
          3833303636376266373338643135323236643230353232660a363238636539343831343433393462
          39666163386664643835646562636433373531666430373335316661643537303737616435643961
          6430353039636364340a383361653133643732366430303262373665643633646336383939326362
          38343338396630343036393430643263646534363266323737343265336664323963
Encryption successful

Include the variable in your ansible playbook and use it:

---
- hosts: all
  vars_files:
    - 'protected.yml'
  vars:
    the_pass_3: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          61663935383430323364373037386234346236323637343030303835373466663733613262636664
          3833303636376266373338643135323236643230353232660a363238636539343831343433393462
          39666163386664643835646562636433373531666430373335316661643537303737616435643961
          6430353039636364340a383361653133643732366430303262373665643633646336383939326362
          38343338396630343036393430643263646534363266323737343265336664323963

  tasks:
    - name: copy ssl certificates to nginx folder
      copy: src=/home/myuser/projects/web-example/{{ certname1 }}.crt dest=/etc/ssl/nginx/{{ certname1 }}.crt owner=root group=root mode=400 backup=yes
      tags: certificates

    - name: copy the password file of the certificate
      copy: content="{{ pass1 }}" dest=/etc/ssl/nginx/{{ certname1 }}.psw owner=root group=root mode=400 backup=yes
      tags: certificates

    - debug:
        msg: Cert - {{ certname1 }} and the variable the_pass_3 - {{ the_pass_3 }}
      tags: mydebug

And executing with the output result:

myuser@srv ~ $ ansible-playbook --ask-vault-pass --tags certificates,mydebug -l srv3 -i ./inventory.ini ./playbook-example-2.yml
Vault password: 

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [srv3]

TASK [copy ssl certificates to nginx folder] *******************************************************************************************************************************
ok: [srv3]

TASK [copy the password file of the certificate] ***************************************************************************************************************************
ok: [srv3]

TASK [debug] ***************************************************************************************************************************************************************
ok: [srv3] => {
    "msg": "Cert - ALL.example.com and the variable the_pass_3 - SoC6ahGheech@aengei3"
}

PLAY RECAP *****************************************************************************************************************************************************************
srv3                       : ok=4    changed=0    unreachable=0    failed=0 

3) You can also encrypt a whole file with the ansible vault

Protect your private key in the file “private.key” with a password:

myuser@srv ~ $ ansible-vault encrypt private.key
New Vault password: 
Confirm New Vault password: 
Encryption successful
myuser@srv ~ $ cat private.key 
$ANSIBLE_VAULT;1.1;AES256
31306239393333363166326436323864333365353938313234666434656562386662643234623736
3833653739373433643438633933303137313432363561630a653666623265386166363634613937
30333765643336343264666662313266636236643064656635663466336138323330396530373036
3434636336396235330a353035306161313764343637353633363761326134363065643364323131
35396463656266343163643132616431613062333336633262343835313266316435

4) View an encrypted file

You can view an ansible encrypted file with the view command:

myuser@srv ~ $ ansible-vault view protected.yml
Vault password:
---
certname1: "ALL.mytv.bg"
pass1: "toh9ti3wai&maem5yen9Ung8ip0Fie"
pass2: "Eid0AiY~ae6ais8aecei7oow#u8Ke7"

All encrypted content (no matter, a whole file or an encrypted variable within an ordinary YAML playbook file) has the same header “$ANSIBLE_VAULT” so it easy to search for the occurrence of it if you need to change all your protected content on a compromised password!

More on ansible vault subject here – https://docs.ansible.com/ansible/latest/user_guide/vault.html

Simple debug

In the last example with the vault ansible playbook we included a debug task to print a variable:

    - debug:
        msg: Cert - {{ certname1 }}

And here is what you can expect:

TASK [debug] ***************************************************************************************************************************************************************
ok: [srv3] => {
    "msg": "Cert - ALL.example.com"
}

Leave a Reply

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