Making a Pipeline

Assuming you have the container running, you should be able to set up a playbook. Before this, you should set up the link with Github. I don't remember exactly how I did that either, so I found a guide . Turns out it's easy to checkout, it was the part about the Repo needing credentials that was hard. I remember it took a little while to get the Github SSH keys all set up, but now that I have them it's trivial. Here is an old playbook I wrote before learning how to use Ansible to set up my first PXE server. Since this was before I figured out Ansible, I was just using Jenkins to automate my shell commands. Once I learned how to use Ansible, I abandoned this way of using Jenkins. I haven't ran this pipeline in a long time, but I believe it did work in the end.  

 

 Jenkinsfile.old_pxe 

 pipeline {

 agent any

 environment {

 DEBIAN_IP = 'debian_machine_ip'

 SSH_CREDENTIALS_ID = 'jenkins-ssh-key'

 PXE_AUTH = 'PXE_AUTH'

 }

 stages {

 stage('Install Packages') {

 steps {

 script {

 sshagent([env.SSH_CREDENTIALS_ID]) {

 sh """

 ssh-keygen -f "/root/.ssh/known_hosts" -R "${params.host_ip}"

 ssh -o StrictHostKeyChecking=no root@${params.host_ip} << EOF

 echo "samba-common samba-common/workgroup string WORKGROUP" | debconf-set-selections

 echo "samba-common samba-common/dhcp boolean true" | debconf-set-selections

 echo "samba-common samba-common/do_debconf boolean true" | debconf-set-selections

 apt update --yes

 apt upgrade --yes

 apt install --yes isc-dhcp-server curl jq tftpd-hpa apache2 syslinux-common net-tools samba pwgen cifs-utils unzip

 

 """

 }

 }

 }

 }

 stage('Configure DHCP Server') {

 steps {

 withCredentials([string(credentialsId: env.PXE_AUTH, variable: 'PXE_AUTH')]) {

 sshagent([env.SSH_CREDENTIALS_ID]) {

 sh """

 ssh -o StrictHostKeyChecking=no root@${params.host_ip} << EOF

 systemctl stop isc-dhcp-server

 curl -o /etc/dhcp/dhcpd.conf -L https://$PXE_AUTH@mattifactory.com/dhcp/dhcpd.conf

 curl -o /etc/default/isc-dhcp-server -L https://$PXE_AUTH@mattifactory.com/dhcp/isc-dhcp-server

 systemctl start isc-dhcp-server

 """

 }

 }

 }

 }

 stage('Configure TFTP Server') {

 steps {

 withCredentials([string(credentialsId: env.PXE_AUTH, variable: 'PXE_AUTH')]) {

 sshagent([env.SSH_CREDENTIALS_ID]) {

 sh """

 ssh -o StrictHostKeyChecking=no root@${params.host_ip} << EOF

 systemctl stop tftpd-hpa

 mkdir -p /srv/tftp

 chown -R tftp:tftp /srv/tftp

 chmod -R 777 /srv/tftp

 curl -o /etc/default/tftpd-hpa -L https://$PXE_AUTH@mattifactory.com/dhcp/tftpd-hpa

 systemctl start tftpd-hpa

 """

 }

 }

 }

 }

 stage('Configure HTTP Server') {

 steps {

 withCredentials([string(credentialsId: env.PXE_AUTH, variable: 'PXE_AUTH')]) {

 sshagent([env.SSH_CREDENTIALS_ID]) {

 sh """

 ssh -o StrictHostKeyChecking=no root@${params.host_ip} << EOF

 systemctl stop apache2

 mkdir -p /var/www/html/debian-installer/amd64

 cd /var/www/html/debian-installer/amd64

 wget -q https://$PXE_AUTH@mattifactory.com/dhcp/netboot.tar.gz

 tar -xzf netboot.tar.gz

 systemctl start apache2

 """

 }

 }

 }

 }

 stage('Configure PXE Boot Configuration') {

 steps {

 withCredentials([string(credentialsId: env.PXE_AUTH, variable: 'PXE_AUTH')]) {

 sshagent([env.SSH_CREDENTIALS_ID]) {

 sh """

 ssh -o StrictHostKeyChecking=no root@${params.host_ip} << EOF

 cp /var/www/html/debian-installer/amd64/pxelinux.0 /srv/tftp/

 cp /usr/lib/syslinux/modules/bios/* /srv/tftp/

 cp -R /var/www/html/debian-installer/amd64/debian-installer /srv/tftp/

 mkdir -p /srv/tftp/pxelinux.cfg

 curl -o /srv/tftp/debian-installer/amd64/linux -L https://$PXE_AUTH@mattifactory.com/dhcp/linux

 curl -o /srv/tftp/debian-installer/amd64/initrd.gz -L https://$PXE_AUTH@mattifactory.com/dhcp/initrd.gz

 curl -o /srv/tftp/debian-installer/amd64/pxelinux.cfg/default -L https://$PXE_AUTH@mattifactory.com/dhcp/default

 curl -o /srv/tftp/pxelinux.cfg/default -L https://$PXE_AUTH@mattifactory.com/dhcp/default

 curl -o /srv/tftp/debian-installer/amd64/pxelinux.cfg/default -L https://$PXE_AUTH@mattifactory.com/dhcp/default

 curl -o /srv/tftp/debian-installer/amd64/grub/grub.cfg -L https://$PXE_AUTH@mattifactory.com/dhcp/grub.cfg

 curl -o /var/www/html/preseed.cfg -L https://$PXE_AUTH@mattifactory.com/dhcp/preseed.cfg

 curl -o /srv/tftp/preseed.cfg -L https://$PXE_AUTH@mattifactory.com/dhcp/preseed.cfg

 """

 }

 }

 }

 }

 stage('Configure SMB & Hostname & Reboot') {

 steps {

 withCredentials([string(credentialsId: env.PXE_AUTH, variable: 'PXE_AUTH')]) {

 sshagent([env.SSH_CREDENTIALS_ID]) {

 sh """

 ssh -o StrictHostKeyChecking=no root@${params.host_ip} << EOF

 mkdir -p /media/share

 chmod 777 /media/share 

 systemctl stop smbd.service

 curl -o /etc/samba/smb.conf -L https://$PXE_AUTH@mattifactory.com/smb/smb.conf

 systemctl start smbd.service

 echo cosmos-pxe > /etc/hostname

 echo 127.0.0.1 cosmos-pxe >> /etc/hosts

 sleep 2

 reboot now

 """

 }

 }

 }

 }

 }

} 

   

 

 With that old example of how to use Jenkins without Ansible behind us, here is a simple Jenkisfile that first injects the Ansible SSH key into a new NanoPi Device and runs a super simple playbook . Ansible requires an inventory file; assuming you're not actually keeping an Ansible inventory file, then you need to generate an inventory file. This is usually done by script ran in the Jenkinsfile. This  inventory.sh script takes a list of IPs and creates an inventory file that  ansible-playbook can read among other things.  This is less important than what the Jenkinsfile shows; specifically this shows how to run commands with variables, how the SSH key works, and how to run a very simply Ansible playbook.  

 I recently updated the dynamic inventory generation script to have a few more options for passing user and group, as well as prohibiting lists of endpoints. This is because I want to open my Jenkins up a bit, and I don't want people running pipelines on my servers. It did make the script a lot more complicated. 

 

 Jenkinsfile 

 pipeline {

 agent any

 

 // Define parameters

 parameters {

 string(name: 'host_ip', description: 'Target System Address')

 }

 environment {

 ANSIBLE_FORCE_COLOR = '1'

 jenkins_public_key = credentials('jenkins_public_key') 

 }

 options {

 ansiColor('xterm')

 }

 stages {

 stage('Inject Auth Key') {

 steps {

 script{

 // clear ssh keys

 echo "Target IP: ${params.host_ip}"

 

 sh """

 ssh-keygen -f "/root/.ssh/known_hosts" -R "${params.host_ip}"

 """

 

 sh """

 echo Copy public key to pi home dir

 sshpass -p 'pi' ssh -o StrictHostKeyChecking=no pi@${params.host_ip} "echo ${env.jenkins_public_key} > /home/pi/authorized_keys"

 """

 sh """

 echo Make sure /root/.ssh exists

 sshpass -p 'pi' ssh -o StrictHostKeyChecking=no pi@${params.host_ip} "echo pi | sudo -S mkdir -p /root/.ssh/"

 """

 

 sh """

 echo Move public key to root

 sshpass -p 'pi' ssh -o StrictHostKeyChecking=no pi@${params.host_ip} "echo pi | sudo -S mv /home/pi/authorized_keys /root/.ssh/authorized_keys"

 """

 

 sh """

 echo Restrict permissions on file

 sshpass -p 'pi' ssh -o StrictHostKeyChecking=no pi@${params.host_ip} "echo pi | sudo -S chmod -R 600 /root/.ssh/"

 """

 

 sh """

 echo Set owner to root

 sshpass -p 'pi' ssh -o StrictHostKeyChecking=no pi@${params.host_ip} "echo pi | sudo -S chown -R root:root /root/.ssh/"

 """

 }

 }

 }

 stage('Generate Inventory File') {

 steps {

 // Generate the dynamic inventory file

 sh """

 jenkins_group=\$(echo ${env.BUILD_USER_GROUPS} | sed 's/,/\\n/g' | grep Jenkins | head -n 1)

 jenkins_user=\$(echo ${env.BUILD_USER})

 cd /var/jenkins_home/ansible

 chmod +x /var/jenkins_home/ansible/inventory/inventory.sh

 /var/jenkins_home/ansible/inventory/inventory.sh -s -g \$jenkins_group -u \$jenkins_user -i ${params.host_ip} 

 """

 }

 }

 stage('Ansible Check') {

 steps {

 sh """

 echo ${params.host_ip}

 hash=\$(echo -n ${params.host_ip} | md5sum | cut -c 1-8)

 inventory_file="/var/jenkins_home/ansible/.inv/inventory-\$hash.yml"

 cd /var/jenkins_home/ansible

 ansible-playbook -i \$inventory_file \

 /var/jenkins_home/ansible/playbooks/pi-init.yaml --ssh-common-args='-o StrictHostKeyChecking=no' 

 

 """

 }

 }

 }

 post {

 always {

 // Remove dynamic Inventory file

 sh """

 hash=\$(echo -n "${params.host_ip}" | md5sum | cut -c 1-8)

 inventory_file="/var/jenkins_home/ansible/.inv/inventory-\$hash.yml"

 rm \$inventory_file

 

 """

 }

 }

}

 

 

 

 pi-init.yaml 

 ---

- name: Ansible Test

 hosts: all

 become: yes

 # this is meant just as a tiny playbook to run after the public key is injected with jenkins

 tasks: 

 # Check System Architecture

 - name: Check CPU Arch

 shell: "dpkg --print-architecture"

 register: cpu_architecture_output

 - name: Display cpu_architecture_output variable

 debug: 

 msg: "{{ cpu_architecture_output.stdout_lines[0] }}"

... 

 

 

 inventory.sh 

 #!/bin/bash

# Dynamic inventory generation script ansible

# Function to display usage

usage() {

 echo "Usage: $0 -i IP_LIST -u JENKINS_USER -g JENKINS_GROUP [-s] [-v]"

 echo "Options:"

 echo " -i IP_LIST Comma-separated list of IPs"

 echo " -u JENKINS_USER Jenkins user for SSH access"

 echo " -g JENKINS_GROUP Jenkins group for SSH access"

 echo " -s Set variable to true if more than one IP is passed"

 echo " -v Display Ansible Version"

 exit 1

}

# Initialize variables with default values

skip=false

more_than_one=false

display_version=false

# Parse command line options

while getopts ":i:u:g:sv" opt; do

 case ${opt} in

 i ) # process option i

 IP_LIST=$OPTARG

 ;;

 u ) # process option u

 JENKINS_USER=$OPTARG

 ;;

 g ) # process option g

 JENKINS_GROUP=$OPTARG

 ;;

 s ) # process option s

 skip=true

 ;;

 v ) # process option v

 display_version=true

 ;;

 \? ) usage

 ;;

 esac

done

shift $((OPTIND -1))

# Check if all required options are provided

if [ -z "$IP_LIST" ] || [ -z "$JENKINS_USER" ] || [ -z "$JENKINS_GROUP" ]; then

 usage

fi

if $display_version; then

 echo "Showing ansible version"

 ansible --version

fi

# Generate an 8-character hash from the IP list

hash=$(echo -n "$IP_LIST" | md5sum | cut -c 1-8)

echo "IP List:"

echo $IP_LIST

echo $hash

# Define the inventory file path with the hash

inventory_file="/var/jenkins_home/ansible/.inv/inventory-$hash.yml"

if $skip; then

 IFS=',' read -ra IPS <<< "$IP_LIST"

 if [ ${#IPS[@]} -gt 1 ]; then

 more_than_one=true

 fi

fi

if $skip; then

 echo "Single host option set"

 if $more_than_one; then

 echo "IP list provided, inventory will be emptied"

 IP_LIST=""

 fi

fi

# Initialize the YAML inventory content

inventory_content="---

all:

 hosts:

"

# Loop through each IP in the comma-separated list

IFS=',' read -ra IPS <<< "$IP_LIST"

for IP in "${IPS[@]}"; do

 inventory_content+=" ${IP}:

 ansible_user: root

"

done

inventory_content+=" vars:

 ansible_connection: ssh

 ansible_ssh_private_key_file: /var/jenkins_home/jenkins_key

 ansible_python_interpreter: /usr/bin/python3

 jenkins_user: '${JENKINS_USER}'

 jenkins_group: '${JENKINS_GROUP}'

"

# Write the inventory content to the file

echo "$inventory_content" > $inventory_file

echo "Inventory file created at $inventory_file with the following content:"

cat $inventory_file

 

 

 

 This is how to configure this to pull from Github: