Scope: apt-install Ansible, create an ansible user on the controller, provision remotes, and run a small project.
- Install Ansible and create the controller user
# as administrator
sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install ansible
# create the local ops user that will run playbooks
sudo adduser --gecos "" ansible
- Generate the controller SSH key (run as ansible)
sudo su - ansible
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
mkdir -p ~/.ansible/facts   # for fact cache path used below
- Bootstrap each remote (run as controller ansible)
Replace jenkins.maksonlee.com as needed and repeat per host.
SERVER=jenkins.maksonlee.com  # or your target host
ssh -tt administrator@"$SERVER" 'sudo bash -c "
  set -euo pipefail
  id -u ansible >/dev/null 2>&1 || adduser --disabled-password --gecos \"\" ansible
  passwd -l ansible || true
  printf \"%s\n\" \"ansible ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/90-ansible
  chmod 440 /etc/sudoers.d/90-ansible
  visudo -cf /etc/sudoers.d/90-ansible >/dev/null
"'
scp ~/.ssh/id_ed25519.pub administrator@"$SERVER":/tmp/ansible.pub
ssh -tt administrator@"$SERVER" 'sudo bash -c "
  set -euo pipefail
  mkdir -p /home/ansible/.ssh
  chmod 700 /home/ansible/.ssh
  chown ansible:ansible /home/ansible/.ssh
  install -o ansible -g ansible -m 600 /tmp/ansible.pub /home/ansible/.ssh/authorized_keys
  rm -f /tmp/ansible.pub
"'
Smoke test:
ssh ansible@"$SERVER" 'whoami && sudo whoami && hostname'
# expect: ansible / root / <hostname>
- Use this project layout
ansible-playbooks/
├─ .ansible/
├─ .vscode/
│  └─ settings.json
├─ group_vars/
│  └─ .gitkeep
├─ host_vars/
│  └─ .gitkeep
├─ playbooks/
│  ├─ haproxy.yml
│  ├─ letsencrypt.yml
│  └─ test.yml
├─ roles/
│  └─ .gitkeep
├─ .gitignore
├─ ansible.cfg
└─ hosts.yml
ansible.cfg
[defaults]
inventory = ./hosts.yml
remote_user = ansible
private_key_file = ~/.ssh/id_ed25519
host_key_checking = False
interpreter_python = auto_silent
forks = 20
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ~/.ansible/facts
[ssh_connection]
pipelining = True
Notes:
- host_key_checking = Falseis fast but less strict; if you prefer verification, set it to- Trueand pre-add host keys with- ssh-keyscan.
- Ensure the cache dir exists: mkdir -p ~/.ansible/facts.
hosts.yml
all:
  hosts:
    thingsboard.maksonlee.com: {}
    kafka.maksonlee.com: {}
    jenkins.maksonlee.com: {}
    spark.maksonlee.com: {}
    client.maksonlee.com: {}
    cdlee-miniu.maksonlee.com: {}
  children:
    haproxy:
      hosts:
        jenkins.maksonlee.com: {}
        thingsboard.maksonlee.com: {}
    letsencrypt:
      hosts:
        jenkins.maksonlee.com: {}
        thingsboard.maksonlee.com: {}
- Sample playbooks
playbooks/test.yml
- name: Connectivity + privilege check
  hosts: letsencrypt
  gather_facts: true
  tasks:
    - name: Ping
      ansible.builtin.ping:
    - name: Who am I?
      ansible.builtin.command: whoami
      changed_when: false
      register: who
    - ansible.builtin.debug:
        msg: "Remote user: {{ who.stdout }}"
    - name: Check sudo
      become: true
      ansible.builtin.command: whoami
      changed_when: false
      register: rootwho
    - ansible.builtin.debug:
        msg: "Sudo user: {{ rootwho.stdout }}"
- Run it (controller ansible user)
ansible@client:~/ansible-playbooks$ /usr/bin/ansible-playbook  /home/ansible/ansible-playbooks/playbooks/test.yml
PLAY [Connectivity + privilege check] **********************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************
ok: [jenkins.maksonlee.com]
ok: [thingsboard.maksonlee.com]
TASK [Ping] ************************************************************************************************************************************************************
ok: [thingsboard.maksonlee.com]
ok: [jenkins.maksonlee.com]
TASK [Who am I?] *******************************************************************************************************************************************************
ok: [jenkins.maksonlee.com]
ok: [thingsboard.maksonlee.com]
TASK [ansible.builtin.debug] *******************************************************************************************************************************************
ok: [jenkins.maksonlee.com] => {
    "msg": "Remote user: ansible"
}
ok: [thingsboard.maksonlee.com] => {
    "msg": "Remote user: ansible"
}
TASK [Check sudo] ******************************************************************************************************************************************************
ok: [jenkins.maksonlee.com]
ok: [thingsboard.maksonlee.com]
TASK [ansible.builtin.debug] *******************************************************************************************************************************************
ok: [jenkins.maksonlee.com] => {
    "msg": "Sudo user: root"
}
ok: [thingsboard.maksonlee.com] => {
    "msg": "Sudo user: root"
}
PLAY RECAP *************************************************************************************************************************************************************
jenkins.maksonlee.com      : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
thingsboard.maksonlee.com  : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
