sudo dnf makecache
sudo dnf install -y epel-releasesudo dnf makecache
sudo dnf install -y ansible
Resource: https://linuxhint.com/install_ansible_centos8
I used an Ubuntu 20.04 instance for this. While I do provide installation instructions for Red Hat, everything is focused around Ubuntu. If you want to use another OS, you’ll just need to change the commands for installing and the username (ubuntu).
ssh-keygen -b 4096 -f ~/.ssh/control_node -N ''
If you want to use SCP, you could use the following function
(don’t forget to change your management key from mgmt_key.pem
to whatever you call it, or omit the -i ~/.ssh/mgmt_key.pem if
you’re using passwords for SSH):
# Used to add the pub key to a managed node
add_pub_key() {
managed_node=$1
scp -i ~/.ssh/mgmt_key.pem ~/.ssh/control_node.pub ubuntu@$managed_node:
ssh -i ~/.ssh/mgmt_key.pem ubuntu@$managed_node \
'cat ~/control_node.pub | tee -a ~/.ssh/authorized_keys && rm ~/control_node.pub'
}
add_pub_key "your_ip_address_goes_here"
sudo apt-get update && sudo apt-get install -y ansible
dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
dnf -y install --enablerepo epel-playground ansible
Add any managed nodes to /etc/ansible/hosts. If you’re just
managing one node, you could do something like this:
[dev_machines]
<managed node ip> ansible_user=ubuntu ansible_ssh_private_key_file=/home/ubuntu/.ssh/mgmt_key.pem
Your config file should at a minimum point to your inventory like so:
[defaults]
inventory = /etc/ansible/hosts
Run this command to ensure that you’re able to authenticate to your managed nodes:
Resources:
# Ubuntu
sudo apt-get upgrade -y
sudo apt-get install -y ansible-lint
# Mac OS
brew install ansible-lint
Resource: https://zoomadmin.com/HowToInstall/UbuntuPackage/ansible-lint
- name: Go to the folder and execute command
command: chdir=/opt/tools/temp ls
This will create docker.list in /etc/apt/sources.list.d/ and
updates the apt repo to reflect this change:
- name: Configure docker apt repo
apt_repository:
repo: "deb https://download.docker.com/linux/debian stretch stable"
state: present
filename: "docker"
update_cache: "yes"
become: true
This creates a variable called custom_vim_plugins
and assigns it a multi-line string after the |
custom_vim_plugins: |
[[custom_plugins]]
name = 'hashivim/vim-terraform'
merged = 0
- name: Creates directory
file:
path: /src/www
state: directory
Resource: https://stackoverflow.com/questions/22844905/how-to-create-a-directory-using-ansible
- name: "Find path to freddy jar"
find:
paths:"{{ ansible_env.HOME }}/burpExtensions/freddy_deserialization_bug_finder/"
patterns: "*.jar"
recurse: "yes"
file_type: file
register: freddy_path
ansible-playbook site.yml -l localhost -vvv --user=<user> --ask-pass
---
- name: A playbook
hosts: localhost
connection: local
become: yes
roles:
- role: your_role
Resource: https://www.middlewareinventory.com/blog/run-ansible-playbook-locally/
ansible-playbook site.yml -l web -vvv
ansible hosts file:
[web]
host.domain ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa.pem
If your host has multiple versions of python and you want to specify a particular one to use:
[kali]
host.domain.com ansible_user=kali ansible_ssh_private_key_file=~/.ssh/id_rsa.pem
ansible_python_interpreter=/usr/bin/python3
Resource: https://stackoverflow.com/questions/59380824/how-to-choose-a-python-interpreter-for-ansible-playbook
ansible-playbook -vvv site.yml -e "ansible_python_interpreter=/usr/bin/python3"
Resource: https://github.com/cisagov/kali-packer/blob/develop/src/packer.json
In this example, we will use a hosts file found in the same directory as site.yml:
ansible-playbook site.yml -i hosts
ansible-playbook site.yml -C -vvv
https://docs.ansible.com/ansible/2.5/user_guide/playbooks_best_practices.html
Add this to your ansible.cfg:
roles_path = roles.galaxy:roles
This will install all ansible galaxy roles in a directory called roles.galaxy
You can keep local roles in the roles folder.
Resource: https://stackoverflow.com/questions/22201306/ansible-galaxy-roles-install-in-to-a-specific-directory
Ansible Collections is a way of packaging and distributing playbooks, roles, modules, and plugins.
Here is a guide on how you can start creating an Ansible Collection:
Install Ansible: Make sure you have installed the latest version of Ansible. You can install Ansible using pip:
python3 -m pip install ansible
Create a Collection: Use the ansible-galaxy command-line tool which comes with Ansible, to create a new collection structure:
ansible-galaxy collection init namespace.collection_name
Replace namespace with your own namespace, and collection_name with your
desired collection name. This command will create a new directory
structure like this:
namespace/
└── collection_name/
├── docs/
├── galaxy.yml
├── plugins/
│ ├── modules/
│ ├── inventory/
│ └── ...
├── README.md
└── roles/
docs/: Local documentation for the collection.galaxy.yml: The file contains all the metadata about the collection
(author, support level, tags, etc.).plugins/: Directory for Ansible plugins.README.md: A README file containing information about the collection.roles/: Directory for Ansible roles.Move or Copy Your Roles and Playbooks: Now you can start moving your existing roles and playbooks into the new collection structure. Make sure you maintain the recommended structure of roles within Ansible.
Update Collection Metadata: Edit the galaxy.yml file to reflect the metadata of your collection. It is important because it provides information about the collection when publishing or sharing.
Build the Collection: Use the following command to build your collection:
ansible-galaxy collection build
This command will create a .tar.gz file which can be uploaded to Ansible Galaxy.
Publish the Collection: If you wish to share your collection, you can upload it to Ansible Galaxy with the following command:
ansible-galaxy collection publish ./namespace-collection_name-1.0.0.tar.gz
Add this to your ansible.cfg:
collections_paths = roles.collections:roles
This will install all ansible galaxy roles in a directory called roles.collections.
Your requirements.yml will probably look somewhat like this if you add in collections:
# Install Docker
roles:
- name: geerlingguy.docker
collections:
- name: community.docker
source: https://galaxy.ansible.com
You can trigger installing any collections listed with:
ansible-galaxy collection install -r requirements.yml
Resources:
Collections best practicesansible-best-practices-using-project-local-collections-and-roles)
This will generate the folders and files associated with a new role. Newer versions of molecule:
ROLE_NAME=l50.mysweetrole
molecule init role "${ROLE_NAME}"
Older versions of molecule:
molecule init role --role-name "${ROLE_NAME}"
molecule converge
# Verbose output:
molecule -vvv converge
There are a lot of ways to skin this cat. I appreciate the format that geerlingguy uses in his projects. It’s simple and gets the job done:
molecule
└── default
├── converge.yml
└── molecule.yml
The verification piece happens in the post_tasks section of converge.yml:
post_tasks:
- name: Test that package is installed
ansible.builtin.stat:
# Read from `vars/main.yml`:
path: "{{ package_path }}"
become: true
Resource: https://github.com/geerlingguy/ansible-role-postgresql
To fix this error:
ERROR: Failed to pre-validate.
{'driver': [{'name': ['unallowed value docker']}]}
run this command:
pip3 install molecule-docker
Resource: https://github.com/ansible-community/molecule/issues/2891
Add the following to a block of code that’s failing the molecule idempotency check:
There is a security sacrifice to keep in mind with doing this.
That being said, add these lines to the ansible.cfg:
[defaults]
host_key_checking = False
Resource: https://stackoverflow.com/questions/32297456/how-to-ignore-ansible-ssh-authenticity-checking
This will cause Ansible to automatically to search for the
password specified in the file specified for ANSIBLE_VAULT_PASSWORD_FILE.
export ANSIBLE_VAULT_PASSWORD_FILE=/path/to/vaultpass.txt
vaultpass.txt should simply contain the password:
r3allyCompl3xp@ssw0rd!thatislongandrandomandallthegoodthings
ansible-vault encrypt file
ansible-vault decrypt file
Resources: https://docs.ansible.com/ansible/latest/user_guide/vault.html#encrypt-string-for-use-in-yaml https://docs.ansible.com/ansible/latest/user_guide/playbooks_vault.html
---
- name: Decrypt template
local_action: "shell {{ view_encrypted_file_cmd }}
{{ role_path }}/templates/template.enc > {{ role_path }}/templates/template"
changed_when: False
- name: Deploy template
template:
src=templates/template
dest=/home/user/file
- name: Remove decrypted template
local_action: "file path={{ role_path }}/templates/template state=absent"
changed_when: False
Resource: https://stackoverflow.com/questions/37682928/ansible-un-vault-and-template-a-file
"$(pwd)/hosts"
"${HOME}/.ansible/hosts"
/etc/ansible/hosts
Resource: https://www.quora.com/What-is-the-difference-between-host-and-inventory-file-in-ansible
They are the same thing. If needed, you
can define a different inventory file in the ansible.cfg file like so:
[defaults]
inventory = /etc/ansible/inventory
ansible $HOSTNAME -m ansible.builtin.setup
ansible $HOSTNAME -m ansible.builtin.setup -a "filter=ansible_distribution"
Resource: https://docs.ansible.com/ansible/latest/user_guide/playbooks_vars_facts.html
If you are having connectivity issues and need to specify a username and private key to connect, you will need to do so in the ansible hosts file like so:
<hostname> ansible_user=<username to connect to target> ansible_ssh_private_key_file=/path/to/private/key/file
This can be used to wait for updates to install before kicking off your ansible code.
---
# Updating the cache immediately can conflict with cloud-init scripts and cause failures.
- name: Wait for /var/lib/dpkg/lock-frontend to be released
shell: while lsof /var/lib/dpkg/lock-frontend; do sleep 10; done;
Add this to the ansible.cfg file:
[ssh_connection]
retries=2
Add the timeout field to the ansible.cfg file:
Resource: https://www.tecmint.com/install-and-configure-an-ansible-control-node/
We can use the YAML folding operator > to make this command:
- name: Use service account
shell: gcloud auth activate-service-account "{{ sa_email }}"
--key-file "{{ sa_creds_file }}"
Much more manageable to see what’s going on:
- name: Use service account
shell: >
gcloud auth activate-service-account "{{ sa_email }}"
--key-file "{{ sa_creds_file }}"
This particular example will set the users variable
to whatever the value is for users or to an empty list:
users: "{{ users | default([]) }}"
Resource: https://serverfault.com/questions/805576/how-to-assign-an-empty-value-to-a-variable-in-ansible
- name: Some command to get users
shell: wget http://127.0.0.1:6000/users
# the output of running the script is stored in users
register: users
- name: Store users as a fact to be used later
set_fact:
users: "{{ users.stdout }}"
cacheable: yes
Resources:
If you have a list of dictionaries from a variable that has some attributes that you want to use to create a fact, syou can do the following:
vars/main.yml:
users:
- username: "admin"
usergroup: "admin"
# port 5901 is 1
vnc_num: 1
Next, we will add a random 8 character long password for each VNC user.
tasks/main.yml:
- name: Set vnc_users fact to be used later
set_fact:
vnc_users: "{{ vnc_users | default([]) \
+ [ {'username': item.username, 'vnc_num': item.vnc_num, \
'pass': lookup('password', '/dev/null chars=ascii_letters,digits,punctuation
length=8')}] }}"
# Make fact available to other roles
cacheable: yes
with_items: "{{ users }}"
Resources:
- name: Store users as a fact to be used later
set_fact:
# Split on newlines
users: "{{ users.stdout.split('\n') }}"
cacheable: yes
Resource: https://gist.github.com/VerosK/9853931
This particular example will create users and groups with the same name:
- name: Create group for each user
group:
name: "{{ item }}"
state: present
loop: "{{ users }}"
- name: Create home directories for each user
user:
name: "{{ item }}"
shell: /bin/bash
groups: "{{ item }}"
state: present
loop: "{{ users }}
Resource: https://stackoverflow.com/questions/62358569/ansible-create-users-and-group
{{ variable_name | regex_replace('\"|\"$', '') }}
- name: For each user in the emails list, replace @ with _ and store in a fact called users
set_fact:
users: "{{ emails | map('regex_replace', '@', '_') | list }}"
cacheable: yes
- name: For each user in the users list, replace @ with _ and store in a fact called users
set_fact:
users: "{{ users | map('regex_replace', '\\.', '_') | list }}"
cacheable: yes
Resources:
- name: Store usernames and emails together
set_fact:
users_emails: "{{ users_emails | default([]) + [dict(username=item[0],
email=item[1])] }}"
loop: "{{ users | zip(emails) | list }}"
ansible localhost -m ansible.builtin.setup | tee facts
Resource: https://docs.ansible.com/ansible/latest/user_guide/playbooks_vars_facts.html
Template path example:
src: "{{ ansible_env.HOME }}/roles/your_role/templates/run.sh.j2"
Resource: https://www.middlewareinventory.com/blog/ansible-template-module-example/
Add this in whenever you need to figure out why your code isn’t working:
- debug:
msg: "System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"
Another example where you can specify a name:
- name: "Get $PATH"
debug: msg="{{ lookup('env','PATH') }} is an environment variable"
Another example where we end the playbook run after we print the debug message:
- debug:
msg: "{{ ansible_distribution_release }}"
- meta: end_play
Resources:
ansible-doc community.docker
Resource: https://www.ansible.com/blog/hands-on-with-ansible-collections
---
- name: Prepare VMs for cluster
hosts: vm_host
roles:
- role: dependencies
become: true
- role_to_run_with_non_elevated_privs
ansible_become: false # equivalent of become:
ansible_user: someuser
Resources:
For example, if you want to use the geerlingguy.docker role
for a bunch of Kali systems and not have it affect
your debian systems that you’re also provisioning,
you need to trick it by changing some of the facts for the role.
You may not want those facts to be global for all roles, so you can set them for that particular one:
---
- hosts: all
name: Prepare VMs for cluster
roles:
- role: geerlingguy.docker
become: yes
docker_users: ["ansible"]
ansible_distribution: "debian"
ansible_distribution_release: "buster"
ansible_os_family: "Debian"
- role: provision_debian
become: yes
You can also accomplish this using this syntax as well:
- hosts: all
name: Prepare VMs for cluster
roles:
- { role: geerlingguy.docker, become: yes, docker_users: ['ansible'],
ansible_distribution: 'debian', ansible_distribution_release: 'buster',
ansible_os_family: 'Debian'}
although I don’t think it’s as nice to read.
Resources:
If you need to change the facts for all hosts, you can do do something like this:
---
- name: Prepare VMs for cluster
hosts: vm_host
vars:
ansible_distribution: "debian"
ansible_distribution_release: "buster"
ansible_os_family: "Debian"
roles:
- { role: geerlingguy.docker, become: yes }
I also added the bit that’s used to restart the service to make the example complete.
- name: Disable Root Login
lineinfile:
dest=/etc/ssh/sshd_config
regexp='^PermitRootLogin'
line="PermitRootLogin no"
state=present
backup=yes
notify:
- restart ssh
handlers:
- name: restart ssh
service:
name=sshd
state=restarted
roles/ssh/tasks/main.yml:
---
- name: Generate SSH key
openssh_keypair:
path: "{{ ssh_key_path }}"
type: rsa
size: 4096
state: present
# Don't recreate if one is already
force: no
- name: Set permissions for private key
file:
path: "{{ ssh_key_path }}"
owner: "{{ user }}"
group: "{{ user }}"
mode: 0600
- name: Set permissions for public key
file:
path: "{{ ssh_key_path }}.pub"
owner: "{{ user }}"
group: "{{ user }}"
mode: 0600
roles/ssh/vars/main.yml:
user: username
key_type: rsa
ssh_key_filename: "{{ user }}_ssh_{{ key_type }}"
ssh_key_path: "/home/{{ user }}/.ssh/{{ ssh_key_filename }}"
Resource:
Folder structure:
role_name
├── tasks
│ └── main.yml
└── vars
├── kali-rolling.yml
├── Debian-10.yml
└── Ubuntu-20.yml
In main.yml:
---
- name: Include Kali specific variables
include_vars: "{{ ansible_distribution_release }}.yml"
when: ansible_distribution == 'Kali GNU/Linux'
- name: Include Distribution version specific variables
include_vars: "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml"
Resources:
One great use for this is if you have multiple users with multiple attributes:
vars/main.yml:
vnc_users:
- username: "{{ ansible_facts['ansible_user_id'] }}"
usergroup: "{{ ansible_facts['ansible_user_id'] }}"
sudo: true
# port 5901
vnc_num: 1
- username: "someuser"
usergroup: "someuser"
sudo: true
# port 5902
vnc_num: 2
tasks/main.yml:
- name: Create .vnc dirs
file:
path: "/home/{{ item.username }}/.vnc"
state: directory
mode: 0755
owner: "{{ item.username }}"
group: "{{ item.usergroup | default(item.username) }}"
with_items: "{{ vnc_users }}"
Resource: https://github.com/sdarwin/Ansible-VNC/blob/master/vars/Debian-10.yml
If you have a problem that is not solvable through what Ansible offers you, STOP the StackOverflow hell and write your own module instead. A little bit of python goes a long way.
First, grab the data structures you’ll be messing with in ansible and just write some python around them to figure out your logic. In my case, I wanted to add a new key to a list of dictionaries from a list.
test.py:
list_of_dicts = [{"username": "bob", "usergroup": "bob", "session": 1},
{"username": "jim",
"usergroup": "jim", "session": 2}]
# uids
list_to_merge = ["1000", "1001"]
for a, b in zip(list_of_dicts, list_to_merge):
a['uid'] = b
print(list_of_dicts)
Great, it works! Now to make it into an ansible module for a role,
create a folder under the role name called library:
Next, add your code to a python file with a descriptive
name using your work in test.py:
library/merge_list_of_dicts_w_list.py:
#!/usr/bin/python3
from ansible.module_utils.basic import AnsibleModule
import json
def run_module():
module_args = dict(
list_of_dicts = dict(type='list', required=True),
list_to_merge = dict(type='list', required=True)
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
ld = module.params['list_of_dicts']
l = module.params['list_to_merge']
for a, b in zip(ld, l):
a['uid'] = b
module.exit_json(changed=False, result=ld)
def main():
run_module()
if __name__ == '__main__':
main()
To call the module from ansible, add the following to your_role/tasks/main.yml:
- name: Merge uids into vnc_users
merge_list_of_dicts_w_list:
list_of_dicts: "{{ vnc_users }}"
list_to_merge: "{{ uids }}"
register: vnc_users_uid
# show output to show that it works
- debug: msg="{{ vnc_users_uid.result }}"
# Get the values from the username keys
- debug: msg="{{ item.username }}"
with_items: "{{ vnc_users_uid.result }}"
Resources:
vars/main.yml:
install_packages:
- curl
- jq
- tmux
tasks/main.yml:
- name: Install dependencies
apt:
name: "{{ install_packages }}"
state: present
update_cache: yes
environment: "DEBIAN_FRONTEND: noninteractive"
# If you have an OS specific task:
when: ansible_distribution_release == "kali-rolling"
Resource: https://github.com/sdarwin/Ansible-VNC/blob/master/tasks/main.yml
This will take a list (packages) and turn it into a space-delimited string:
"{{ packages | join (' ') }}"
One example of where this could be useful (cause I can’t get this environment
variable to work with the apt module,
and no one else on the internet seems to have either):
- name: Install dependencies
# DO NOT put quotes around the {{ }}, or the command wont' work
shell: DEBIAN_FRONTEND=noninteractive apt-get -y install {{ packages |
join (' ') }}
Resource: https://stackoverflow.com/questions/47244834/how-to-join-a-list-of-strings-in-ansible
If you need to copy a file for a role, you’ll need the following folders:
├── files
│ └── yourfile
├── tasks
│ └── main.yml
Put the file(s) in the files directory.
In tasks/main.yml, you’ll need to use the copy module to get
the file into place on the managed node. For example:
- name: Create file
copy:
src: yourfile # you don't need to point to the files directory,
# just put the file name here
dest: "/root/yourfile"
mode: 0755
owner: root
group: root
If you need a template for a role, you’ll need the following folders:
├── templates
│ └── yourtemplate.j2
├── tasks
│ └── main.yml
The template file needs to have a .j2 extension.
Here’s an example that sets up a systemd job to run VNC for multiple users (vncserver.j2):
[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target
[Service]
Type=forking
PAMName=login
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill :{{ item.item.vnc_num }} >
/dev/null 2>&1 || :'
ExecStart=/usr/bin/vncserver :{{ item.item.vnc_num }} {{ vnc_client_options }} {{
vnc_client_options_per_user | default() }}
ExecStop=/usr/bin/vncserver -kill :{{ item.item.vnc_num }}
[Install]
WantedBy=default.target
You can then call it in your main.yml with the template module, like so:
- name: Update per-user systemd service files
template:
src: vncserver.j2
dest: "/home/{{ item.item.username }}/.config/systemd/user/vncserver.service"
mode: 0644
owner: "{{ item.item.username }}"
group: "{{ item.item.usergroup | default(item.item.username) }}"
when:
- vnc_ansible_managed_startup_scripts or not item.stat.exists
with_items: "{{ checksystemd.results }}"
Resources:
If you want to run a play or task after main.yml has finished,
you can put that file into the tasks directory and then use the
following to call it:
- name: Configure systemd auto-start service
include: systemd.yml
Resource: https://github.com/sdarwin/Ansible-VNC/blob/master/tasks/main.yml
The first place to define variables with the least precedence
will be in defaults/main.yml.
These can be overwritten with (for example) variables defined
in a task (taskname/vars/main.yml).
A full precedence list can be found here: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html
Resources:
- name: Check if test_file exists
stat:
path: $HOME/test_file
register: test_file
- name: Report if test_file exists
debug:
msg: "The file exists"
when: test_file.stat.exists
Resource: https://phoenixnap.com/kb/ansible-check-if-file-exists
- name: Configure systemd auto-start service
include: systemd.yaml
when: var.setup_systemd is true
#when: var.setup_systemd is false
- name: Delete a file (or symlink) if it exists
file:
path: $HOME/test_file
state: absent
Resource: https://www.toptechskills.com/ansible-tutorials-courses/ansible-file-module-tutorial-examples/
---
- name: Clone repo
git:
repo: "{{ repo_url }}"
dest: "{{ clone_location }}"
Resource: https://docs.ansible.com/ansible/2.3/git_module.html
- name: Generate temp creds to clone repo
shell: |
git config --global credential.helper '!aws codecommit credential-helper $@'
git config --global credential.UseHttpPath true
- name: Clone repo
git:
repo: "{{ repo_url }}"
dest: "{{ clone_location }}"
Coincidentally, the above also showcases how to run multiple bash commands and break them over multiple lines.
Start by installing the community.docker collection:
ansible-galaxy collection install community.docker
Next, create a role for the deployment, and put the following in it:
├── your-docker-compose-deployment
│ ├── docker-compose.yml
├── tasks
│ └── main.yml
In your-docker-compose-deployment, you’ll want to have
the folder with your docker compose project.
In tasks/main.yml, you’ll want the following to install required
dependencies and then run the deployment:
---
- name: Install docker sdk
pip:
name: docker
- name: Install docker-compose
pip:
name: docker-compose
- name: Create and start services
docker_compose:
Note that pip will default to whatever interpreter you’ve specified.
Resources:
This can be useful if you want to use .env for a docker-compose deployment,
for example.
env.j2:
POSTGRES_PASSWORD={{ password }}
Snippet to put into your role:
- name: Generate password
become: true
become_user: "{{ docker_user }}"
template:
src: env.j2
dest: "{{ docker_compose_deployment_location }}/.env"
vars:
password: "{{ lookup('password', '/dev/null chars=ascii_letters,digits,
punctuation length=50') }}"
Resources:
This is an example taken from the nginx ansible role:
{% for path in nginx_logrotate_conf.paths %}
{{ path }}
{% endfor %}
{
{% for option in nginx_logrotate_conf.options %}
{{ option }}
{% endfor %}
postrotate
{% if ansible_facts['os_family'] == "Debian" %}
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
{% else %}
nginx -s reopen
{% endif %}
endscript
}
Resources:
If you need a custom python function for a template, you’ll want to use an ansible filter plugin.
You will have to expose this code to all roles
by putting it in a filter_plugins folder like so:
/etc/ansible/
├── ansible.cfg
├── filter_plugins
│ └── get_user.py
├── hosts
├── roles
get_user.py:
#!/usr/bin/python3
class FilterModule(object):
def filters(self):
return {
'get_user': self.get_user
}
def get_user(self, users, username, birthdate):
for user in users:
if username in user.values() and user['birthdate'] == birthdate:
return user
You can call it in a template like so:
{{ list_of_users_in_a_dictionary | get_user('mikey','01/01/1988') }}
The first variable for the function, users,
is defined to the left of the pipe.
The second (username) and third (birthdate)
are defined as you would normally define them for a
function call: get_user('mikey','01/01/1988').
You can also assign the output to a variable and then use that:
{% set user = list_of_users_in_a_dictionary | get_user('mikey','01/01/1988') %}
{{ user.info }}
{{ user.birthdate }}
Similar to modules, write the code first outside of ansible, and then integrate it.
Check the template that’s generated and make sure it’s working as you expect it to.
Resources:
roles/pipexample/tasks/main.yml:
- name: Install pip packages
pip:
name: "{{ item }}"
state: present
loop: "{{ pip_packages }}"
roles/pipexample/vars/main.yml:
pip_packages:
- package_name
- another_package_name
roles/gemexample/tasks/main.yml:
- name: Install gems
gem:
name: "{{ item }}"
user_install: no
loop: "{{ gems }}"
roles/gemexample/vars/main.yml:
gems:
- gem_name
- another_gem_name
- name: Run a bash script
become: true
# Run as the ubuntu user
become_user: ubuntu
shell: bash some_script.sh
args:
chdir: "/home/ubuntu"
This does not appear to work well on cloud - see Query metadata if you’re running on a cloud instance.
Resource: https://stackoverflow.com/questions/39819378/ansible-get-current-target-hosts-ip-address
- debug: msg="{{ lookup('env','HOME') }}"
Resource: https://stackoverflow.com/questions/43126400/ansible-host-how-to-get-the-value-for-home-variable
roles/ip/tasks/main.yml:
---
- name: Get aws public IP
uri:
method: GET
url: "http://169.254.169.254/latest/meta-data/public-ipv4"
return_content: yes
register: uri_output
- name: Print public IP
debug:
msg: "Public IP: {{ uri_output.content | regex_replace('\n','') }}"
roles/ip/tasks/main.yml:
---
- name: Get aws public IP
uri:
method: GET
url: "http://169.254.169.254/latest/meta-data/public-ipv4"
return_content: yes
register: uri_output
- name: Public IP to template
template:
src: ip.j2
dest: "/tmp/pub_ip.txt"
vars:
public_ip: "{{ uri_output.content | regex_replace('\n','') }}"
ip.j2:
PUBLIC_IP={{ public_ip }}
Resources:
This example will add the tools made available by MSF
(such as pattern_create.rb) for exploit dev to the global $PATH:
- name: Add MSF tools to PATH
lineinfile:
path: /etc/zsh/zshrc
line: export PATH=$PATH:/usr/share/metasploit-framework/tools/exploit
when: ansible_distribution_release == "kali-rolling"
Resource: https://www.mydailytutorials.com/ansible-add-line-to-file/
Get the env var for the currently connected user:
chdir: "{{ ansible_user_dir }}"
Another way (I’ve gotten errors around this before in certain situations like calling ansible from user-data):
chdir: "{{ ansible_env.HOME }}"
Note: both of these will return /root if you use become:.
Get user’s home directory via env (this
will get the user that you connected with
if you’re using become):
chdir: "{{ lookup('env','HOME') }}"
Debug line:
- debug:
msg: "One way: {{ ansible_user_dir }} | Another way: {{ ansible_env.HOME }}
| A final way: {{ lookup('env', 'HOME') }}"
Resources:
- name: Download and extract AWS CLI
unarchive:
src: "{{ aws_download_server }}"
dest: "{{ ansible_user_dir }}"
remote_src: yes
Resource: https://docs.ansible.com/ansible/2.5/modules/unarchive_module.html
{ { ansible_hostname|upper } }
{ { ansible_hostname|lower } }
This particular example will install the Amazon SSM agent on a debian system.
- name: Download and install SSM Agent
apt:
deb: https://s3.{{ region }}.amazonaws.com/amazon-ssm-{{ region }}/latest/{{
ansible_distribution|lower }}_amd64/amazon-ssm-agent.deb
Resource: https://stackoverflow.com/questions/22939775/ansible-and-wget
- blockinfile:
insertafter: EOF
path: ce_hostname.conf
block: "{{ lookup('template', 'nokia_t1_port.j2') }}"
marker: ""
Resource: https://stackoverflow.com/questions/63725020/ansible-template-append-to-file
This is equivalent to the lsb_release -sc bash command:
- name: Add {{ ansible_distribution_release }} PPA repository for Mozilla Firefox
ansible.builtin.apt_repository:
repo: ppa:mozillateam/ppa
codename: "{{ ansible_distribution_release }}"
Resource: https://www.shellhacks.com/ansible-lsb_release-variable/