Installing Rancher with ansible - Lesson 3

Author: Stu Feeser

Introduction

In this lesson, we’re going to automate the installation of Rancher using Ansible. If you’ve been following along, you’ve likely installed Rancher manually and know that setting up authentication can be tedious.

That’s where Puppeteer comes in! Instead of manually configuring the Rancher UI, we’ll use a Node.js script with Puppeteer to handle password setup for us.

By the end of this post, you’ll have:

✅ A fully automated Rancher installation using Ansible
Puppeteer automatically configuring your Rancher password
✅ A deeper understanding of how to structure a modular playbook


Make sure you have the puppeteer script from the second lesson.

📜 Click here to view the full puppeteer script
const fs = require('fs');
const puppeteer = require('puppeteer');

// ✅ Step 1: Get the new password from the command line
const newPassword = process.argv[2];

if (!newPassword) {
  console.error("� Error: Please provide a new password as a command-line
argument.");
  console.error("Usage: node rancher_setup.js 'YourNewPassword123'");
  process.exit(1);
}

// ✅ Step 2: Read the hash password from the file
const passwordFilePath = '/home/student/pswd.txt';

if (!fs.existsSync(passwordFilePath)) {
  console.error(`� Error: Password file not found at ${passwordFilePath}`);
  console.error("Please create the file and add the hash password.");
  console.error("Example: echo 'yourhashedpassword' > /home/student/pswd.txt");
  process.exit(1);
}

const hashPassword = fs.readFileSync(passwordFilePath, 'utf8').trim();

// ✅ Step 3: Start Puppeteer Automation
(async () => {
  const browser = await puppeteer.launch({
    headless: true, // Set to false for debugging
    ignoreHTTPSErrors: true,
    args: [
      '--ignore-certificate-errors',
      '--no-sandbox',
      '--disable-setuid-sandbox'
    ]
  });

  const page = await browser.newPage();

  // -------------------------------
  // Step 4: Open Rancher Setup Page
  // -------------------------------
  console.log("Opening initial Rancher setup page...");
  await page.goto('https://controller-1', { waitUntil: 'networkidle2', timeout:
10000 });

  // Wait for the password field (the one expecting the default hash)
  await page.waitForSelector('#password', { visible: true, timeout: 10000 });
  console.log("Filling in the hash password...");
  await page.type('#password', hashPassword);

  // Wait for and click the submit button
  await page.waitForSelector('#submit', { visible: true, timeout: 10000 });
  console.log("Submitting hash password...");
  await page.click('#submit');

  // -------------------------------
  // Step 5: Change the Default Password
  // -------------------------------
  const radioSelector = 'span[aria-label="Set a specific password to use"]';
  await page.waitForSelector(radioSelector, { visible: true, timeout: 10000 });
  await page.click(radioSelector);

  console.log(`Setting new password: ${'*'.repeat(newPassword.length)}`);

  const newPasswordSelector = 'div[data-testid="setup-password"]
input[type="password"]';
  await page.waitForSelector(newPasswordSelector, { visible: true, timeout:
10000 });
  await page.type(newPasswordSelector, newPassword);

  const confirmPasswordSelector = 'div[data-testid="setup-password-confirm"]
input[type="password"]';
  await page.waitForSelector(confirmPasswordSelector, { visible: true, timeout:
10000 });
  await page.type(confirmPasswordSelector, newPassword);

  // -------------------------------
  // Step 6: Accept EULA Agreement
  // -------------------------------
  console.log("Checking the EULA agreement...");
  const eulaSelector = 'div[data-testid="setup-agreement"]
label[for="checkbox-eula"] span.checkbox-custom';
  await page.waitForSelector(eulaSelector, { visible: true, timeout: 10000 });
  await page.click(eulaSelector);

  // -------------------------------
  // Step 7: Submit the Form
  // -------------------------------
  console.log("Taking full-page screenshot before clicking the continue
button...");
  await page.screenshot({ path: 'pre_submit.png', fullPage: true });

  console.log("Clicking the continue button...");
  await page.click('#submit button');

  await new Promise(resolve => setTimeout(resolve, 5000));

  console.log("Setup complete.");
  await browser.close();
})();

Breaking Down the Ansible Playbook

Before diving into the full playbook, let’s break it down step by step. This will make it easier to understand the different tasks we need to accomplish.


Ensure that all necessary dependencies are installed, including:

curl and gnupg (required for setting up Node.js)
✅ Node.js 18 from the NodeSource repository
✅ Puppeteer dependencies (various libraries)
✅ Docker (to run Rancher)

Step 1: Installing and Running Rancher on controller-1

The first playbook section installs and runs Rancher on the controller-1 node. Here we install docker, and run a daemonized Rancher container.

📌 Ansible Tasks:

- name: Install Rancher on controller-1
  hosts: controller-1
  become: true
  tasks:
    - name: Install Docker for Rancher to run in a container
      apt:
        name: docker.io
        state: present
        update_cache: yes

    - name: Start and enable Docker service
      systemd:
        name: docker
        state: started
        enabled: yes

    - name: Run Rancher container on controller-1 ### not idempotent
      command:
        cmd: >
          docker run -d --restart=unless-stopped --name rancher 
          -p 80:80 -p 443:443 --privileged rancher/rancher:v2.6.9          
      args:
        creates: /var/lib/docker/containers/rancher
      register: rancher_run
      changed_when: rancher_run.rc == 0

This ensures Rancher is installed inside a Docker container and ready to go.


Step 2: Retrieving and Copying the Rancher Bootstrap Password

Once Rancher starts, we give it a bit of time to finish initializing. We then extract the initial password hash from the logs and transfer it to the the bchd machine.

📌 Ansible Tasks:

    - name: Wait for Rancher to initialize before moving on
      pause:
        seconds: 120

    - name: Get Rancher Bootstrap Password
      shell: |
        docker logs rancher 2>&1 | grep "Bootstrap Password" | awk '{print $6}'        
      register: rancher_password
      changed_when: false

    - name: Save Bootstrap Password
      copy:
        content: "{{ rancher_password.stdout }}"
        dest: /home/student/pswd.txt
        owner: student
        group: student
        mode: '0600'

    - name: Fetch Rancher current password from controller to localhost
      fetch:
        src: /home/student/pswd.txt
        dest: /home/student/pswd.txt
        flat: yes

This ensures the Rancher password is accessible on the bchd machine. It will be used shortly to verify access and allow us to set it to what we want.


Step 3: Setting Up Puppeteer on bchd

Now that we have the Rancher password, we can automate logging in and updating the passwword using Puppeteer. This play runs against our localhost, bchd. First we install the required packages and libraries, then create our project directories.

📌 Ansible Tasks:

- name: Setup Puppeteer on bchd
  hosts: localhost
  become: true
  vars:
    new_rancher_password: "passwordpassword"
    puppeteer_project_path: "/home/student/puppeteer-project"
    puppeteer_script: "update_rancher_passwd_w_puppeteer.js"

  tasks:
    - name: Install required system packages for node.js
      apt:
        name:
          - curl
          - gnupg
        state: present
        update_cache: yes

    - name: Add NodeSource repository
      shell: |
        curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -        
      args:
        executable: /bin/bash

    - name: Install required libraries for Puppeteer
      apt:
        name:
          - nodejs
          - libatk1.0-0
          - libatk-bridge2.0-0
          - libcups2
          - libxkbcommon-x11-0
          - libgbm1
          - libasound2
          - libnss3
          - libxcomposite1
          - libxrandr2
          - libxdamage1
          - libpango-1.0-0
          - libpangocairo-1.0-0
          - libgdk-pixbuf2.0-0
          - libegl1
          - libgcrypt20
          - libx11-xcb1
          - libgtk-3-0
        state: present
        update_cache: yes

    - name: Create Puppeteer project directory
      file:
        path: "{{ puppeteer_project_path }}"
        state: directory
        owner: student
        group: student
        mode: '0755'

Step 4: Running the Puppeteer Script

Finally, we execute the Puppeteer script to update the Rancher password. We go ahead and initialize the project, install Puppeteer, and run the puppeteer script we created in lesson two.

📌 Ansible Tasks:

    - name: Initialize Node.js project
      shell:
        cmd: npm init -y
        chdir: "{{ puppeteer_project_path }}"
        creates: "{{ puppeteer_project_path }}/package.json"

    - name: Install Puppeteer
      shell:
        cmd: npm install puppeteer
        chdir: "{{ puppeteer_project_path }}"
        creates: "{{ puppeteer_project_path }}/node_modules/puppeteer"

    - name: Run Puppeteer script to update Rancher password
      shell:
        cmd: "node {{ puppeteer_project_path }}/{{ puppeteer_script }} '{{
new_rancher_password }}'"
      args:
        chdir: "{{ puppeteer_project_path }}"
      register: puppeteer_result

    - name: Debug Puppeteer script output
      debug:
        var: puppeteer_result.stdout

Full Ansible Playbook

For reference, here is the entire Ansible playbook, Put together piece by piece. Click below to expand to view the full file. This should be copiable if you are following along.

📜 Click here to view the full playbook
- name: Install Rancher on controller-1
  hosts: controller-1
  become: true
  tasks:
    - name: Install Docker for Rancher to run in a container
      apt:
        name: docker.io
        state: present
        update_cache: yes

    - name: Start and enable Docker service
      systemd:
        name: docker
        state: started
        enabled: yes

    - name: Run Rancher container on controller-1 not idemopotent
      command:
        cmd: >
          docker run -d --restart=unless-stopped --name rancher 
          -p 80:80 -p 443:443 --privileged rancher/rancher:v2.6.9          
      args:
        creates: /var/lib/docker/containers/rancher
      register: rancher_run
      changed_when: rancher_run.rc == 0

    - name: Wait for Rancher to initialize before moving on
      pause:
        seconds: 120

    - name: Get Rancher Bootstrap Password
      shell: |
        docker logs rancher 2>&1 | grep "Bootstrap Password" | awk '{print $6}'        
      register: rancher_password
      changed_when: false

    - name: Save Bootstrap Password
      copy:
        content: "{{ rancher_password.stdout }}"
        dest: /home/student/pswd.txt
        owner: student
        group: student
        mode: '0600'

    - name: fetch Rancher current password from controller to localhost
      fetch:
        src: /home/student/pswd.txt
        dest: /home/student/pswd.txt
        flat: yes

- name: Setup Puppeteer on bchd
  hosts: localhost
  become: true
  vars:
    new_rancher_password: "passwordpassword"  # Update this or pass via extra
vars
    puppeteer_project_path: "/home/student/puppeteer-project"
    puppeteer_script: "update_rancher_passwd_w_puppeteer.js"

  tasks:
    - name: Install required system packages for node.js
      apt:
        name:
          - curl
          - gnupg
        state: present
        update_cache: yes

    - name: Add NodeSource repository
      shell: |
        curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -        
      args:
        executable: /bin/bash

    - name: Install required libraries for Puppeteer
      apt:
        name:
          - nodejs
          - libatk1.0-0
          - libatk-bridge2.0-0
          - libcups2
          - libxkbcommon-x11-0
          - libgbm1
          - libasound2
          - libnss3
          - libxcomposite1
          - libxrandr2
          - libxdamage1
          - libpango-1.0-0
          - libpangocairo-1.0-0
          - libgdk-pixbuf2.0-0
          - libegl1
          - libgcrypt20
          - libx11-xcb1
          - libgtk-3-0
        state: present
        update_cache: yes

    - name: Create Puppeteer project directory
      file:
        path: "{{ puppeteer_project_path }}"
        state: directory
        owner: student
        group: student
        mode: '0755'

    - name: Initialize Node.js project
      shell:
        cmd: npm init -y
        chdir: "{{ puppeteer_project_path }}"
        creates: "{{ puppeteer_project_path }}/package.json"

    - name: Install Puppeteer
      shell:
        cmd: npm install puppeteer
        chdir: "{{ puppeteer_project_path }}"
        creates: "{{ puppeteer_project_path }}/node_modules/puppeteer"

    - name: Run Puppeteer script to update Rancher password
      shell:
        cmd: "node {{ puppeteer_project_path }}/{{ puppeteer_script }} '{{ new_rancher_password }}'"
      args:
        chdir: "{{ puppeteer_project_path }}"
      register: puppeteer_result

    - name: Debug Puppeteer script output
      debug:
        var: puppeteer_result.stdout

Create your inventory file

vim inventory.ini

[rancher]
controller-1 ansible_user=student ansible_become=true

Run the Playbook

ansible-playbook -i inventory.ini install_rancher.yml

Login to rancher with your new password

  • User: admin
  • Password: passwordpassword

Conclusion

This lesson took our manual Rancher setup and turned it into a fully automated deployment with Ansible and Puppeteer. Now, whenever you need to install Rancher, just run the playbook, sit back, and watch it configure itself! 🚀

← Previous