Skip to content

Instantly share code, notes, and snippets.

@sony-level
Last active March 30, 2026 16:06
Show Gist options
  • Select an option

  • Save sony-level/cc7cea1bbc154009949fde84b00b5c27 to your computer and use it in GitHub Desktop.

Select an option

Save sony-level/cc7cea1bbc154009949fde84b00b5c27 to your computer and use it in GitHub Desktop.
Automated install ansible
#!/usr/bin/env bash
set -Eeuo pipefail
# ============================================================
# _ _ _ ____ ___ ____ _ _____
# / \ | \ | / ___|_ _| __ )| | | ____|
# / _ \ | \| \___ \| || _ \| | | _|
# / ___ \| |\ |___) | || |_) | |___| |___
# /_/ \_\_| \_|____/___|____/|_____|_____|
#
# Ansible Installer
#
# Description:
# Cross-platform Ansible installation script designed to
# automate installation using the official recommended method
# for each supported operating system.
#
# Features:
# - Automatic OS detection
# - Official package source setup
# - Ansible PPA GPG key installation and verification
# - Debian/Ubuntu fingerprint validation
# - Ansible post-install verification
# - Bash/Zsh autocomplete support
# - Timestamped logs
#
# Website : https://level-sony.fr
# Contact : https://level-sony.fr/en/contact/
# GitHub : https://github.com/sony-level
# ============================================================
readonly SCRIPT_NAME="$(basename "$0")"
readonly SITE_NAME="level-sony.fr"
readonly SITE_CONTACT_URL="https://level-sony.fr/en/contact/"
readonly ANSIBLE_PPA_KEY_URL="https://keyserver.ubuntu.com/pks/lookup?fingerprint=on&op=get&search=0x6125E2A8C77F2818FB7BD15B93C4A3FD7BB9C367"
readonly ANSIBLE_KEYRING="/usr/share/keyrings/ansible-archive-keyring.gpg"
readonly ANSIBLE_APT_LIST="/etc/apt/sources.list.d/ansible.list"
readonly EXPECTED_ANSIBLE_FINGERPRINT="6125E2A8C77F2818FB7BD15B93C4A3FD7BB9C367"
readonly LOG_TS_FORMAT="+%Y-%m-%d %H:%M:%S"
log() {
local level="$1"
shift
printf '[%s] [%s] [%s] %s\n' "$(date "$LOG_TS_FORMAT")" "$level" "$SCRIPT_NAME" "$*"
}
info() {
log "INFO" "$@"
}
warn() {
log "WARN" "$@" >&2
}
error() {
log "ERROR" "$@" >&2
}
die() {
error "$@"
error "Support / contact: ${SITE_CONTACT_URL}"
exit 1
}
on_error() {
local exit_code=$?
error "Script failed at line ${BASH_LINENO[0]} with exit code ${exit_code}."
error "Website: ${SITE_NAME} | Contact: ${SITE_CONTACT_URL}"
exit "$exit_code"
}
trap on_error ERR
require_command() {
local cmd="$1"
command -v "$cmd" >/dev/null 2>&1 || die "Required command not found: $cmd"
}
run_sudo() {
if [[ "${EUID}" -eq 0 ]]; then
"$@"
else
sudo "$@"
fi
}
normalize_fingerprint() {
tr -d '[:space:]' | tr '[:lower:]' '[:upper:]'
}
verify_ansible() {
info "Verifying Ansible installation..."
require_command ansible
ansible --version >/dev/null 2>&1 || die "ansible --version failed."
info "Ansible installation verified successfully."
}
detect_shell_rc_file() {
local current_shell shell_name
current_shell="${SHELL:-}"
shell_name="$(basename "$current_shell")"
case "$shell_name" in
bash) echo "${HOME}/.bashrc" ;;
zsh) echo "${HOME}/.zshrc" ;;
*) echo "" ;;
esac
}
enable_autocomplete() {
local rc_file
rc_file="$(detect_shell_rc_file)"
if ! command -v ansible >/dev/null 2>&1; then
warn "Ansible is not available in PATH yet. Skipping autocomplete."
return 0
fi
if [[ -z "$rc_file" ]]; then
warn "Unsupported shell for automatic autocomplete setup. Skipping."
return 0
fi
if [[ ! -f "$rc_file" ]]; then
info "Creating shell configuration file: $rc_file"
touch "$rc_file"
fi
info "Ansible autocomplete is typically enabled by default. Restart your shell to activate it."
}
install_macos() {
info "Detected macOS."
require_command brew
info "Installing Ansible with Homebrew..."
brew install ansible
verify_ansible
enable_autocomplete
}
install_windows_choco() {
info "Detected Windows-like environment with Chocolatey available."
require_command choco
info "Installing Ansible with Chocolatey..."
choco install ansible -y
verify_ansible
}
get_linux_id() {
if [[ -r /etc/os-release ]]; then
. /etc/os-release
echo "${ID:-unknown}"
else
echo "unknown"
fi
}
get_linux_like() {
if [[ -r /etc/os-release ]]; then
. /etc/os-release
echo "${ID_LIKE:-}"
else
echo ""
fi
}
get_debian_codename() {
local codename
if [[ -r /etc/os-release ]]; then
. /etc/os-release
if [[ -n "${UBUNTU_CODENAME:-}" ]]; then
codename="$UBUNTU_CODENAME"
elif [[ -n "${VERSION_CODENAME:-}" ]]; then
codename="$VERSION_CODENAME"
fi
fi
if [[ -z "$codename" ]] && command -v lsb_release >/dev/null 2>&1; then
codename="$(lsb_release -cs)"
fi
if [[ -z "$codename" ]]; then
die "Unable to determine Debian/Ubuntu codename."
fi
# Map Raspberry Pi Ubuntu and unsupported codenames to supported versions
case "$codename" in
questing) echo "noble" ;; # Raspberry Pi Ubuntu -> Ubuntu 24.04
trixie) echo "noble" ;; # Debian 13 -> Ubuntu 24.04
bookworm) echo "jammy" ;; # Debian 12 -> Ubuntu 22.04
bullseye) echo "focal" ;; # Debian 11 -> Ubuntu 20.04
buster) echo "bionic" ;; # Debian 10 -> Ubuntu 18.04
noble|jammy|focal|bionic) echo "$codename" ;;
*) die "Unsupported codename for Ansible installation: ${codename}" ;;
esac
}
verify_ansible_gpg_fingerprint() {
info "Verifying Ansible PPA GPG fingerprint..."
local fingerprint
fingerprint="$(
gpg --no-default-keyring \
--keyring "$ANSIBLE_KEYRING" \
--fingerprint --with-colons 2>/dev/null \
| awk -F: '/^fpr:/ { print $10; exit }'
)"
[[ -n "$fingerprint" ]] || die "Unable to read GPG fingerprint from $ANSIBLE_KEYRING"
fingerprint="$(printf '%s' "$fingerprint" | normalize_fingerprint)"
if [[ "$fingerprint" != "$EXPECTED_ANSIBLE_FINGERPRINT" ]]; then
die "Ansible PPA GPG fingerprint mismatch. Expected: $EXPECTED_ANSIBLE_FINGERPRINT | Actual: $fingerprint"
fi
info "Ansible PPA GPG fingerprint verified successfully."
}
install_ubuntu() {
info "Detected Ubuntu."
run_sudo apt-get update
run_sudo apt-get install -y software-properties-common curl wget lsb-release
info "Adding Ansible PPA..."
run_sudo add-apt-repository --yes --update ppa:ansible/ansible
info "Installing Ansible..."
run_sudo apt-get install -y ansible
verify_ansible
enable_autocomplete
}
install_debian() {
info "Detected Debian."
# Clean up any stale Ansible PPA config from previous runs to prevent
# apt-get update failures (e.g. sqv SHA1 rejection on Trixie+).
[[ -f "$ANSIBLE_APT_LIST" ]] && run_sudo rm -f "$ANSIBLE_APT_LIST"
[[ -f "$ANSIBLE_KEYRING" ]] && run_sudo rm -f "$ANSIBLE_KEYRING"
run_sudo apt-get update
local codename=""
if [[ -r /etc/os-release ]]; then
. /etc/os-release
codename="${VERSION_CODENAME:-}"
fi
# Debian Trixie+ uses Sequoia PGP (sqv) which rejects SHA1 binding
# signatures in the Ansible PPA InRelease. Use pipx instead.
case "$codename" in
trixie|forky)
install_debian_pipx
return
;;
esac
run_sudo apt-get install -y gnupg curl wget lsb-release ca-certificates
# Remove existing keyring to avoid interactive gpg --dearmor prompt
[[ -f "$ANSIBLE_KEYRING" ]] && run_sudo rm -f "$ANSIBLE_KEYRING"
info "Downloading Ansible PPA GPG key..."
wget -O- "$ANSIBLE_PPA_KEY_URL" | run_sudo gpg --dearmor -o "$ANSIBLE_KEYRING"
verify_ansible_gpg_fingerprint
local ubuntu_codename
ubuntu_codename="$(get_debian_codename)"
info "Configuring Ansible PPA for Debian (using Ubuntu ${ubuntu_codename})..."
echo "deb [signed-by=${ANSIBLE_KEYRING}] http://ppa.launchpad.net/ansible/ansible/ubuntu ${ubuntu_codename} main" \
| run_sudo tee "$ANSIBLE_APT_LIST" >/dev/null
info "Refreshing APT package index..."
run_sudo apt-get update
info "Installing Ansible..."
run_sudo apt-get install -y ansible
verify_ansible
enable_autocomplete
}
install_debian_pipx() {
info "Installing Ansible via pipx (PPA unavailable on this Debian version due to SHA1 policy)..."
run_sudo apt-get install -y pipx python3-full
pipx install --include-deps ansible
# Ensure ~/.local/bin is in PATH for verification
export PATH="${HOME}/.local/bin:${PATH}"
pipx ensurepath
verify_ansible
enable_autocomplete
}
install_fedora() {
info "Detected Fedora."
require_command dnf
info "Installing Ansible with DNF..."
run_sudo dnf install -y ansible
verify_ansible
enable_autocomplete
}
install_arch() {
info "Detected Arch Linux."
require_command pacman
info "Installing Ansible with Pacman..."
run_sudo pacman -S ansible
verify_ansible
enable_autocomplete
}
install_opensuse() {
info "Detected openSUSE."
require_command zypper
info "Installing Ansible with Zypper..."
run_sudo zypper install ansible
verify_ansible
enable_autocomplete
}
install_rhel_centos_amzn() {
info "Detected RHEL/CentOS/Amazon Linux family."
if command -v dnf >/dev/null 2>&1; then
info "Using DNF..."
run_sudo dnf install -y epel-release
run_sudo dnf install -y ansible
elif command -v yum >/dev/null 2>&1; then
info "Using YUM..."
run_sudo yum install -y epel-release
run_sudo yum install -y ansible
else
die "Neither dnf nor yum is available."
fi
verify_ansible
enable_autocomplete
}
install_linux() {
local distro like
distro="$(get_linux_id)"
like="$(get_linux_like)"
info "Linux distribution detected: ID=${distro}, ID_LIKE=${like}"
case "$distro" in
ubuntu)
install_ubuntu
;;
debian)
install_debian
;;
fedora)
install_fedora
;;
arch)
install_arch
;;
opensuse-leap|opensuse-tumbleweed)
install_opensuse
;;
amzn|rhel|centos|rocky|almalinux|ol)
install_rhel_centos_amzn
;;
*)
case "$like" in
*debian*)
install_debian
;;
*rhel*|*fedora*)
install_rhel_centos_amzn
;;
*)
die "Unsupported Linux distribution: ${distro}. Use manual installation for this system."
;;
esac
;;
esac
}
detect_platform() {
local uname_out
uname_out="$(uname -s 2>/dev/null || true)"
case "$uname_out" in
Darwin) echo "macos" ;;
Linux) echo "linux" ;;
MINGW*|MSYS*|CYGWIN*) echo "windows_like" ;;
*)
if command -v choco >/dev/null 2>&1; then
echo "windows_like"
else
echo "unknown"
fi
;;
esac
}
main() {
info "Starting Ansible installation..."
info "This script will install Ansible using the official package repositories."
info "Maintainer website: ${SITE_NAME}"
info "Support page: ${SITE_CONTACT_URL}"
info "Platform: $(detect_platform)"
info "Architecture: $(uname -m)"
local platform
platform="$(detect_platform)"
case "$platform" in
macos)
install_macos
;;
linux)
install_linux
;;
windows_like)
install_windows_choco
;;
*)
die "Unsupported operating system."
;;
esac
info "Ansible setup completed successfully."
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment