commit ad75f53a28297fb6dd910c9df2e2768fd039d75a Author: Gilles Mouchet Date: Fri May 30 08:11:49 2025 +0200 v1.0 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6133120 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.fontSize": 13, + "terminal.integrated.fontSize": 13, + "window.zoomLevel": 1.4, + } \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fe02c25 --- /dev/null +++ b/LICENSE @@ -0,0 +1,34 @@ + +Non-Commercial Use License – [sync-awx-inventory.sh] + +Copyright (c) [2025] [Gilles Mouchet] + +This script is provided free of charge with its source code. You are permitted to: + +Use the script for personal, educational, or non-commercial professional purposes. +Study, modify, and share the script freely, provided this license is included. + +You are strictly prohibited from: + +- Selling this script or any modified version. +- Using it in commercial services or products. +- Distributing it in exchange for financial compensation, directly or indirectly. + +This script is provided "as is", without any warranty of any kind. +----------------------------------------------------------------------------------------- +Licence d’utilisation non commerciale – [sync-awx-inventory.sh] + +Copyright (c) [2025] [Gilles Mouchet] + +Ce script est fourni gratuitement avec son code source. Toute personne est autorisée à : + +Utiliser le script à des fins personnelles, éducatives ou professionnelles non commerciales. +Étudier, modifier et partager le script gratuitement, à condition de conserver cette licence. + +Il est strictement interdit de : + +- Vendre ce script ou une version modifiée. +- L’utiliser dans des services ou produits commerciaux. +- Le distribuer en échange d’une contrepartie financière, directe ou indirecte. + +Ce script est fourni "tel quel", sans garantie d’aucune sorte. diff --git a/README.md b/README.md new file mode 100644 index 0000000..887784a --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# Sync-awx-inventory +## Description +This script this script synchronize the hosts source file with an AWX inventory + +## Requirements +- [Rocky 9 GMo](https://gmodocs.dyndns.org/system/rocky/installation/) +- Package + - jq + +## Installation +### Script +```bash +cp sync-awx-inventory /usr/local/bin/ +``` + +### AWX credentials +Authentication by token. See [](https://ansible.readthedocs.io/projects/awx/en/latest/) to create token +```bash +cat << EOF > $HOME/.awxcred +token=werfgg....drf +``` + +### Cron +```bash +echo " */7 * * * * root ./usr/local/bin/sync-awx-inventory.sh > /dev/null 2>&1" > /etc/cron/sync-awx-inventory.cron +``` + +## Usage +```bash +sudo /usr/local/bin/sync-awx-inventory.sh --help +``` +## Logs +```bash +sudo journalctl -f -t syn_awx_inv +``` + +## Changelog +#### [1.0] - 29.05.2025 +##### Added +- Version from scratch by [GMo](mailto:gilles.mouchet@gmail.com) diff --git a/sync-awx-inventory.sh b/sync-awx-inventory.sh new file mode 100755 index 0000000..4264a4a --- /dev/null +++ b/sync-awx-inventory.sh @@ -0,0 +1,257 @@ +#!/bin/bash +############################################################ +# Decription: this script synchronize the hosts source +# file with an AWX inventory +# +# Author: Gilles Mouchet (gilles.mouchet@gmail.com) +# Creation Date: 29.05.2025 +# Version: 1.0 +# +# Install: see README.md +# Usage: ./sync-awx-inventory.sh --help +# Changelog: +# V1.0 - 29.05.2025 - GMo +# Added +# - Creation of script from scratch +# +############################################################ + +version=1.0 +#user=user_name +#pass=secret +cred_file=$HOME/.awxcred + +# awx server +awx_srv=awx +awx_srv_url=https://${awx_srv}.gmolab.net + +# awx inventory name +awx_inventory=linux + +# search domain in hosts file +search_domain=gmolab +search_domain_prefix=net + +# hosts file +src_hosts_file=/etc/hosts +log_tag=syn_awx_inv + +# array of host name in /etc/hosts +hosts_array=() + +#----- FUNCTIONS -----# +# function add host in awx inventory +add_host_in_awx() { #inventories/{id}/hosts/ + api_rc=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$awx_srv_url/api/v2/hosts/" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $awx_token" \ + -d @- < >(tee /dev/tty | logger -t $log_tag) +exec 2> >(tee /dev/tty >&2 | logger -t $log_tag) + +case $1 in + -p|--parameter) + cat << EOF + Credentials file (\$cred_file): $cred_file + Source file (\$src_hosts_file): $src_hosts_file + AWX inventory (\$awx_inventory): $awx_inventory + Domain search in src (\$search_domain): $search_domain.$search_domain_prefix + AWX server url (\$awx_srv_url): $awx_srv_url + Log tag (\$log_tag): $log_tag +EOF + exit 1 + ;; + -h|--help) + cat << EOF +Usage: $(basename "$0") OPTIONS +Synchronizes the hosts source file with an AWX inventory +Options: + -p, --parameter display parameters + -l, --help display this help text and exit + -v, --version display the verion +EOF + exit + ;; + -v|--version) + cat << EOF +$(basename "$0") v$version (c) 1990 - $(date +%Y) by Gilles Mouchet +Non-Commercial Use License – See LICENSE for details +EOF + exit + ;; +esac + +logger -t $log_tag "[INFO] - start script" +#check if awx server is up +if ! nc -z -w 2 $awx_srv 22 2>/dev/null; then + echo "[ERROR] - server '$awx_srv_url' is not up" + exit 1 +fi + +# read awx credential +if [ -f $cred_file ]; then + awx_token=$(grep "token=" $cred_file | cut -d'=' -f2) +else + echo "[ERROR] - file $cred_file not found !" + exit 1 +fi + +#---------- create host list from inventory +# get inventory id +inventory_id=$(curl -s -H "Authorization: Bearer $awx_token" \ + "$awx_srv_url/api/v2/inventories/?name=$awx_inventory"| jq -r '.results[0].id') + +# get inventory items +# this curl display onliy the 25 first host (pagination) +#awx_item=$(curl -s -H "Authorization: Bearer $awx_token" \ +# "$awx_srv_url/api/v2/inventories/$inventory_id/hosts/") + +# this get ALL hosts in inventory +next_url="$awx_srv_url/api/v2/inventories/$inventory_id/hosts/" +while [ -n "$next_url" ] && [ "$next_url" != "null" ]; do + awx_item=$(curl -s -H "Authorization: Bearer $awx_token" -H "Accept: application/json" "$next_url") + + # extract hostnames and add them to the table + mapfile -t new_hosts < <(echo "$awx_item" | jq -r '.results[].name') + hosts_in_awx+=("${new_hosts[@]}") + + next_url=$(echo "$awx_item" | jq -r '.next') + # complete the URL if necessary + if [[ "$next_url" =~ ^/ ]]; then + next_url="$awx_srv_url$next_url" + fi +done + +#---------- create host list from hosts file +# read hosts fiée +while IFS= read -r line; do + hosts_in_file+=("$line") +done < <( grep -v '^#' $src_hosts_file \ + | sed 's/#.*//' \ + | tr -s ' ' \ + | tr '\t' ' ' \ + | cut -d ' ' -f2- \ + | tr ' ' '\n' \ + | grep '\.'$search_domain'\.'$search_domain_prefix'$' \ + | sed 's/\.'$search_domain'\.'$search_domain_prefix'$//' \ + | sort -u +) +# +# for debug only +# test for pagination 30 hosts +#hosts_in_file=(titi gros-minet tom jerry cortex minus road-runner coyote bugs-bunny riri fifi loulou picsou \ +# donald-duck mickey pluto mister-magoo droopy calimero scooby-doo samy daffy-duck popey \ +# woody-woodpecker garfield casper winnie casper homer marge bart) +# test for pagination 25 hosts +#hosts_in_file=(titi gros-minet tom jerry cortex minus road-runner coyote bugs-bunny fifi loulou picsou \ +# donald-duck mickey scooby-doo samy daffy-duck popey \ +# woody-woodpecker garfield casper winnie casper homer marge bart) + +# CAUTION delete all host in AWX inventory +#hosts_in_file=() + +#---------- add host in awx inventory if not exixts +echo "[INFO] - add host to '$awx_inventory' AWX inventory" +action_add=false +# loop on each element of the source host file +for item_host in "${hosts_in_file[@]}"; do + found=false + # loop on each item from awx inventory + for item_awx in "${hosts_in_awx[@]}"; do + # check if host in source exist in awx inventoy + if [[ "$item_host" == "$item_awx" ]]; then + found=true + break + fi + done + # if not found, add host into awx inventory + if ! $found; then + action_add=true + add_host_in_awx $item_host + rc=$? + if [ "$rc" -eq "201" ]; then + echo "[SUCCESS] - host '$item_host' successfully added to '$awx_inventory' AWX inventory" + else + echo "[ERROR] - error $rc when adding '$item_host' to '$awx_inventory' AWX inventory" + #exit 1 + fi + fi +done +if ! $action_add; then + echo "[INFO] - no hosts added to AWX '$awx_inventory' inventory" +fi +echo "" + +#---------- check if hosts from awx exist in the source host file +#---------- if not, delete from awx inventory +echo "[INFO] - delete host from '$awx_inventory' AWX inventory" +action_del=false +# loop on each item from awx inventory +for item_awx in "${hosts_in_awx[@]}"; do + found=false + # loop on each element of the source host file + for item_host in "${hosts_in_file[@]}"; do + # check if host in awx inventory exist in the source host file + if [[ "$item_awx" == "$item_host" ]]; then + found=true + break + fi + done + # if not found, delete host from awx inventory + if ! $found; then + action_del=true + del_host_from_awx $item_awx + rc=$? + if [[ "$rc" == "204" ]]; then + echo "[SUCCESS] - host '$item_awx' successfully deleted from '$awx_inventory' inventory" + else + echo "[ERROR] - error $rc when deleting '$item_awx' from '$awx_inventory' AWX inventory" + exit 1 + fi + fi +done +if ! $action_del; then + echo "[INFO] - no host to delete from '$awx_inventory AWX inventory" +fi +echo "" +echo "[INFO] - the '$awx_inventory' inventory on AWX is up to date" +logger -t $log_tag "[INFO] - start script" +