We could not resist creating another thingOps word with AwesomeOps, but that is the best way to describe automating Windows and RHEL workloads with the combination of Ansible and JFrog. In this blog we will cover the use of Ansible and JFrog as it relates to Windows Server workloads exclusively. Now, while we love RHEL and Linux in general (my work laptops are all System76 running Linux), this blog focuses exclusively on Windows Server because Windows does not get enough automation love. On that note, let’s jump right in!
Why Ansible:
Agentless - arguably the single biggest reason enterprises choose Ansible is because it is agentless. Agentless applications shrink your security surface area and allow security teams to centralize command and control. There are dozens of other reasons why you want to choose agentless, but that is not for this blog post.
Parallel - another great advantage of Ansible is its ability to run tasks in parallel. This is a huge time saver. Parallel processing in Ansible is referred to as forking, or just forks. The default setting of forks is 5, and we recommend you increase this value to something that makes sense for the underlying system executing the code. A word of caution here, in the early days of our automation platform 2 years ago we allowed users to select a number of forks from a pipeline drop-down. This worked well until a user decided to select 100 forks and then run 10 parallel pipelines with 100 forks each. While our Azure Kubernetes (AKS) cluster was able to handle the load, all other builds running on the same AKS cluster as the 10 pipes with 100 forks each all slowed to a crawl.
Idempotent - Ansible code, when written well, is idempotent. This is the ideal solution when you want to ensure running the code achieves the same exact result every time it is executed. This term is commonly misunderstood, but can be easily be understood by comparing two simple math equations.
Example 1 Idempotent:
1*1*1*1
In this example we multiply 1 times 1 4 times consecutively and arrive at 1. This is Idempotent because we always arrive at 1.
Example 2 NOT Idempotent:
10*10*10*10
In this example we multiply 10 times 10 4 times consecutively and arrive at a different number. The initial result is 100, however on the second application of the operation the result is 1,000 and so on.
Ease of Programming - If you have ever had to automate Windows with PowerShell or Puppet or both, I feel your pain. Dozens of days of my life have been lost to puppet programming and forcing Windows to work. Ansible is a super simple abstraction layer that enables infrastructure engineers to rapidly start automating.
Why JFrog:
Binary Version Control - This is an underrated awesome feature of JFrog and it would be great to see this deployed more often in the enterprise. This feature is a game changer for organizations looking to peek out of the dark ages of managing binaries with file shares and manual downloads. The ability to control the version of a binary is incredibly important at scale. Many organizations have strategies around how and when and to what environments binaries get deployed.
Faster Download Speeds
Typically to automate systems and install applications you need to reach out to the internet to identify and download the correct binaries. With JFrog you create an internal cache of all of your binaries so that you download within your network. This will improve automation performance, and, nice side note, will protect your organization from bad things accidentally or purposefully slipping into the latest release of a package.
RBAC - JFrog has role based access control so that organizations can control who has access to artifact repositories.
Kubernetes Ready - We have deployed the JFrog Kubernetes solution multiple times and it is straight fire. We will not go into the details here, but let’s just make it simple. It just works in Kubernetes and it works well.
How Ansible & JFrog Work Together
Over the years we have used many strategies to hook up JFrog and Ansible, and in this blog we will discuss 2 of those methods and why we moved away from one.
Method 1: Binary Loop Downloads
One of the cool features of Ansible is creating a list of items that can be looped over and downloaded to a Windows host in parallel using a single block of code like this.
- name: Download All Binaries From JFrog
win_get_url:
url: “{{ artifactory_url }}/{{ item }}.zip”
dest: “{{ install_dir }}”
loop: “{{ package_path_list }}”
This the code above iterates over the list below and downloads each package.
package_path_list:
- BgInfo
- McAfee
- Centrify
- Crowdstrike
Next we unzip all the files
name: Unzip All Packages
win_shell: “expand-archive -Force {{ install_dir }}\\{{ item }}.zip -DestinationPath {{ install_dir }}”
loop: “{{ package_path_list }}”
Now that you have downloaded your binaries to your system and unzipped them, it is time to install the applications. There are loads of baked in goodies inside of ansible that can assist with getting Windows systems configured quickly.
Let's take BGInfo first.
- name: Configure BgInfo folder
win_file:
path: C:\BgInfo
state: directory
- name: Copy BgInfo
win_copy:
src: 'C:\TempInstall\BgInfo'
dest: C:\
remote_src: yes
Look at how cool that is. Above we have two separate Ansible modules that are doing some PowerShell heavy lifting for us. Both win_file and win_copy are part of the Ansible collection named ansible.windows.colection. This collection needs to be installed on your underlying Ansible execution system, and it contains loads of baked modules that are super simple and effective. To be fair to PowerShell the two operations above would have taken 2 lines of code, however these are simple and clear and self documenting. Checkout more about win_file here https://docs.ansible.com/ansible/latest/collections/ansible/windows/win_file_module.html
and checkout more about win_copy here
- name: Configure BgInfo registry
win_regedit:
path: HKLM:\software\microsoft\windows\currentversion\run
name: BgInfo
type: string
data: c:\bginfo\bginfo.exe /NOLICPROMPT /TIMER:0
state: present
tags: ensure
This is where some of the magic of Ansible happens. The code block above leverages a module named win_regedit, and would have taken something like the following code below to accomplish the same task in PowerShell:
$registryPath = "HKLM:\software\microsoft\windows\currentversion\run"
$Name = "BgInfo"
$value = "c:\bginfo\bginfo.exe /NOLICPROMPT /TIMER:0"
IF(!(Test-Path $registryPath))
{
New-Item -Path $registryPath -Force | Out-Null
New-ItemProperty -Path $registryPath -Name $name -Value $value `
-PropertyType string -Force | Out-Null}
ELSE {
New-ItemProperty -Path $registryPath -Name $name -Value $value `
-PropertyType string -Force | Out-Null}
This is a simple way to get binaries off JFrog and onto your systems. I highly recommend this approach for:
Ephemeral systems
Deployment testing
Smoke testing
If you have any of the above types of systems, we definitely recommend this approach as it ensures you get what you want all of the time with super simple Ansible code.
We would not recommend this approach for long standing Windows servers unless you swap out binaries frequently. On long standing systems this just keeps downloading the same binaries to your system which is a big time sink.
Method 2: Checksum Downloads & Service Checks
While the previous method works well with our packerization platform and our automated VM smoke tests, it does not work well when automating and connecting to thousands of long standing systems. So, we decided to move away from the binary loop download in favor of individual binary checksum validation and downloads. How does this work? Glad you asked!
We found that one of the attributes returned from an Artifactory call with Ansible is the checksum value of the binary we want to download. So we decided to treat each binary as a separate service with its own ansible YML file and configuration tasks.
We start each service now with the following block of code:
- name: BGinfo Download from JFrog
win_get_url:
url: “{{ artifactory_url }}/BGinfo.zip”
dest: “{{ install_dir }}”
validate_certs: false
force: false
use_proxy: false
register: __artifactory
What we are doing here is downloading a BGinfo from JFrog and registering __artifactory, which is used to conditionally control the next execution of the next block of code:
- name: Install and Configure BGinfo if checksums differ
when: (__artifactory.changed | bool == true or (packer | bool ==
true )
block:
This code executes when the checksums do not match, or when we target a packer build. Below the block: are the rest of the tasks. We will not cover all of the tasks here because we do not want to deprive you of the coding fun. We are not being sarcastic here. :) We honestly would not want to deprive you of the fun. And if you are thinking, "just give me the answers I do not have fun coding", we would ask you why are you coding then. You have to love what you do, otherwise why do it? Hehe. Anyway, back to business. The next task that is important for these types of Windows installs is a service check. Just because the binary does not exist, we do not want to assume the service has never been installed and is running.
- name: Get Demo Service Status
win_service:
name: demo
register: __demo_service_status
- name: Print Service Status
debug:
msg:"demo service is {{ __demo_service_status.state }}"
- name: Fail Task if Service is NOT Running
fail:
msg: "demo service is not running"
when: __demo_service_status.state!="running"
This is pretty cool stuff. There is a module called win_service that enables you to do all sorts of things with Windows services such as: restart, set startup mode, pause service, and checking the state of a service. Once we check the demo service, we register __demo_service_status. Then we print the service status, and finally fail a task if the demo service is not running.
There is so much more to cover using Ansible and JFrog to configure Windows systems, but that will have to be for a part 2.
Comentários