30 October 2021

I recently converted an archaic setup to a more modern, automated process. We had a Powershell script that one person would run on their workstation to update a DB that held all of our Citrix servers that Ansible would query for host information. Rather than keep a static host file that needed to be updated manually, a Python script would run and query the database and pull the host information the playbook was asking for. It was a Powershell script that used the Citrix cmdlets to query our environment and populate the database.

The individual left and the process had been in a forgotten state and we almost lost the script because it was only on his old PC. I grabbed it and for a time I was the one running it. We’re starting to use GitHub for our department and we’ve been using Ansible and Jenkins to patch servers for a while… so I decided to create a modern, automated process for this.

Here are some prerequisites to make this work:
1. The account Jenkins runs as needs write permission to your Ansible role folder (This isn’t strictly necessary but I organized the files this way, as you’ll see). I did this by changing the group on the folder to one containing myself and the Jenkins account.
Ex) chgrp team_admins /etc/ansible/roles/myrole
2. The workstation(s) the PS code runs on needs WinRM enabled
enable-psremoting -force
3. You need a host file in Ansible containing the workstation(s) you’ll be using.
4. You need to create an access token in GitHub so Jenkins can make a pull request.
This is found under your account on GitHub > Settings > Developer Settings > Personal access tokens
Generate a new one and give it whatever permissions you want, mine only has “repo” checked.

ANSIBLE CONFIGURATION

HOST FILE
Here’s an example host file, you can save it wherever you want, just note where it is so you have the correct path in the Jenkins config.
workstations.yml

---
  powershell_devices:
    hosts:
      MYWORKSTATION:
      MYWORKSTATION2:

You can add as many hosts as you want but when the Jenkins job runs, the way I’ve set it up, it will only run on the machine you pass to the Jenkins parameter. Even if you only have one machine you need a host file.

ROLE
Create a new folder structure. Here is an example main.yml for the tasks folder. The below role first checks if the path I want to copy it to exists and will create it if it doesn’t. It then copies the PS1 file from the roles directory to the local machine. It then runs the script from the local machine. You may need to either change the Execution Policy on the local machine or use -ExecutionPolicy ByPass in the win_shell. That would then change win_shell to: powershell.exe -ExecutionPolicy ByPass -File C:\ansible\scripts\AnsibleCitrixInventoryQuery.ps1

--- # Run DB update for Ansible's Citrix workflows
  - name: Check if Scripts dir exists
    win_stat:
      path: C:\ansible\scripts
      get_checksum: no
    register: scripts_exists
  
  - name: Create Scripts dir if not exist
    win_shell:  mkdir C:\ansible\scripts
    when: not scripts_exists.stat.exists

  - name: Copy script to local PC
    win_copy:
      src: "{{ role_path }}/files/AnsibleCitrixInventoryQuery.ps1"
      dest: C:\ansible\scripts\AnsibleCitrixInventoryQuery.ps1

  - name: Run the inventory script
    win_shell: C:\ansible\scripts\AnsibleCitrixInventoryQuery.ps1
    become: yes
    become_method: runas

Note that I’m using “become” so that it will execute the script with the credentials passed to the playbook. My Jenkins build will include, under the “Additional parameters”, the path to a credential file that is stored on the Ansible Linux server. This account needs admin rights on the workstation running the PowerShell!

PLAYBOOK
You can then add the role to an existing playbook or create a new playbook with just the role:

---
- hosts:
  - "{{ computer }}"

  gather_facts: no

  roles:
    - ROLENAME

{{ computer }} is a reference to the variable it will be passed from Jenkins (setup below). You can name it whatever you want. Since I’m just performing a script run there is no need to waste time on gathering facts.

JENKINS CONFIGURATION

First thing to do is setup a New Item in Jenkins using the “Freestyle project” template.

GENERAL
Check “This project is parameterized” and created a “computer” parameter for accepting the computer name of the remote machine the script will run on.

SOURCE CODE MANAGEMENT
Select Git
For the repository URL you need to use the address to the repo but paste your Access Token at the beginning with an “@”
Ex) https://ACCESSTOKEN@github.com/REPOPATH

Specify whichever branch you are checking out from. If it defaults to /master you’ll probably need to change it to /main

Click Add next to “Additional Behaviors” and select “Check out to a sub-directory”. This is the optional step that just keeps it a bit organized. The path you enter is relative to the workspace directory, which for me is /var/lib/jenkins/workspaces/jobname. So in the path I have ../../../../../etc/ansible/roles/ROLENAME/files. When it does a pull request it drops the files right in the role’s files directory which makes it easy to copy from the server to the workstation running the script.

BUILD TRIGGERS (Optional)
Check “Build periodically” and set your schedule, I did “H 3 * * *”. This makes it run daily during the 3AM hour.

BUILD
“Add build step” and choose “Invoke Ansible Playbook”. Enter the full path to your playbook YAML file.

Select your usual Vault Credentials.

Then click the “Advanced” button and select “Extra Variables”. This is where you’ll pass the Jenkins parameter for the computer onto the Ansible Playbook. The Key is the name of the variable you want for Ansible (ex: computer) and the Value will be the name of the Jenkins variable inside ${} (ex: ${computer}).

Your Additional Parameters should include the host file with your remote machine(s) passed as the inventory file (ex: -i /etc/ansible/hostfiles/your_host_file.yml). It should also include the credential file that contains credentials for the account that has admin rights on the workstation. (ex: -e @/etc/ansible/admin_creds.yml). If you don’t have a file for that use: ansible-vault create admin_creds.yml. This is important as this is the account Ansible will execute all it’s commands as so it needs rights on those machines.

And that’s it. Now we don’t have to worry about the code getting lost when a person or machine leaves. We don’t need to worry about different versions of the script existing in multiple places (and we can track changes). And more importantly this will run daily and keep our database up to date and we can keep an eye on the job.

It took a while to figure out all the permissions because I wasn’t the one who setup Ansible or Jenkins and the account used to run Jenkins shouldn’t be. Hopefully this all makes sense, if not, just comment with your question and I’ll do my best to answer it!


There are no comments.



You must be logged in to post a comment.

Links

RSS 2.0 Feed

Support

Brave Rewards
This site supports Brave Rewards. Please consider tipping or adding it to your monthly contributions if you find anything helpful!

For other ways: Support

Support this blog! If you have found it helpfu you can send me crypto. Any amount is appreciated!
ETH: 0xBEaF72807Cb5f4a8CCE23A5E7949041f62e8F8f0 | BTC: 3HTK5VJWr3vnftxbrcWAszLkRTrx9s5KzZ | SHIB: 0xdb209f96dD1BdcC12f03FdDfFAD0602276fb29BE
Brave Users you can send me BAT using the browser.