Post Note: I had to use {xml} rather than the correctly formatted square brackets because the syntax highlighter is parsing it and I haven’t found an effective workaround yet. If you wish to copy/paste those snippets, you’ll need to replace the curly braces in {xml} explicitly typed variables with square brackets [ and ].
As you might have surmised from other posts, automating our patch process end to end weighs heavily on my mind. In speaking with many of you at Converge, I found that I’m not alone in this goal (Obsession?). One of our requirements for the Tanium implementation project was that it be able to fit into the fully automated process for patching the Citrix Master images that our non-persistent VDI fleet are spawned from. Tanium has good documentation about what steps need to be taken to generalize the client for this type of fleet but I’d like to talk about finding orchestration waypoints that can be leveraged to allow these things to be built into an automated workflow. For this purpose, the API isn’t really what we want to go with; instead, we want to-the-minute status so that the orchestration workflow is always capable of making the best decision for what to do next in a given moment.
While I don’t feel compelled or even particularly well-qualified to speak about the adjacent mechanisms in the orchestration engines my organization leverages, I do want to point out some of the straightforward ways you can ingest data into such orchestration engines to make decisions in the pursuit of automated workflows. There are a few files that you’ll need to consider for creating an automated workflow leveraging Tanium and an external orchestration engine that can do things like open and close your master images when patching needs to be performed. We’ll be covering 3 primary areas in this post:
- Determining Tanium Patch scan status
- \Tanium Client\Patch\scans\scan-statuses
- Determining Tanium Patch deployment status
- \Tanium Client\Patch\deployments\deployment-statuses
- Determining Tanium Patch updates that were applied
- \Tanium Client\Patch\deployments\deployment-results
As luck would have it, the files we’re needing this information from are all valid XML despite not having file extensions indicating as such. The method for ingesting these files to make decisions against is relatively straightforward but lets closely examine one to start with:
Tanium Patch Scan Status
{xml}$scanStatuses = Get-Content -Path "C:\Program Files (x86)\Tanium\Tanium Client\Patch\scans\scan-statuses"
We’ve created a $scanStatuses variable to hold data gathered by the Get-Content cmdlet in PowerShell. The Get-Content cmdlet is targeting the scan-statuses file with that action so it will be fed into the $scanStatuses variable.
The most notable piece of this is the XML explicit preamble. PowerShell is by default an implicitly typed language that supports explicit typing. You’re likely asking yourself why that matters so let’s expand upon that thought. Dynamic typing and explicit typing are referring to the way we store data in a variable.
In PowerShell, you can leave the interpreter to make an assumption about what kind of data you’re intending for a variable to represent but this can give you confusing results. For the sake of simplicity, lets say you want $x to be equal to 1 so that you can add $x + 1 to get 2. To that end, you type:
$x = '1'
Seems straightforward enough, right? Unfortunately, you’d be mistaken. If you pipe $x to Get-Member ($x | Get-Member in a PowerShell session), you’ll see that it has been identified as TypeName: System.String. Since you quoted the 1, the interpreter viewed the value as a string rather than an integer. If you perform $x + 1, you will get an output of 11 because the interpreter is going to view this as an attempt to concatenate values rather than perform addition since one of the values is a string and the other is an integer. That’s obvious to those of us who have been snagged by these types of dynamic interpreter bear traps before but some of you may not yet have had the pleasure.
While the stakes in this example are rather low and the example itself is simple, you can imagine how this can lead to confusion down the line when you’re needing to do complex operations including multiple variables and the interpreter does something wonky because you were expecting one data type and the interpreter had opted for another.
Explicit typing allows you to avoid that confusion. It’s less pretty but it ensures up front that there is no guesswork involved when you’re working with the values of a lot of variables. It also ensures that methods unique to the data type you want are available to you. The {xml} datatype has a significant amount of methods available to leverage against the data held there that wouldn’t be available to you if you simply cast the value of the file from Get-Content into a dynamically interpreted string variable. For example, executing this call on the {xml}$scanStatuses variable we set earlier will result in the image below:
id, status, and UpdatedAt are all properties of the xml object that you could isolate given that PowerShell natively handles things as objects. For instance, $scanStatuses.ChildNodes.ScanConfiguration | Select-Object -ExpandProperty status would give you an output of Waiting For Scan Interval. With this information and the context of the article, you would know that patching will likely begin soon on this endpoint as this particular status differs from the status indicating that the first scan hasn’t taken place. To my understanding (And subject to alteration due to a wrist-slapping from a TAM), this is a breakdown of the statuses you’ll be dealing with:
Scan Status Explanations
• ‘Downloading’ – The CAB is being downloaded.
• ‘Scanning’ – The CAB has been downloaded and the endpoint is actively in the process of scanning.
• ‘Unenforced’ – The scan configuration for the endpoint has not been enforced. If an endpoint doesn’t have at least one enforced scan configuration, patching won’t work.
• ‘Waiting For Scan Interval’ – The endpoint has scanned before but is time-constrained and will not scan again until the time constraints have been satisfied.
• ‘Waiting For Initial Scan’ – Nothing has happened yet; the endpoint has never been scanned.
Tanium Patch Deployment Status
Tanium Patch deployment statuses rely on their own file just as was the case with the aforementioned scan statuses.
{xml}$deploymentStatuses = Get-Content -Path "C:\Program Files (x86)\Tanium\Tanium Client\Patch\deployments\deployment-statuses" $latestDeployment = $deploymentStatuses.DeploymentStatuses.Deployment | Sort-Object -Property id | Select-Object -First 1
You’re probably seeing what I’m getting at here but lets break it down to make sure we’re all on the same page. I’ve created the $deploymentStatuses variable to ingest the deployment-statuses file into my PowerShell session. Subsequently, I created the $latestDeployment variable, called into the XML elements to expose the individual deployment values, and selected out the array with the highest numerical id property. Calling that variable from the session results in the following output:
Just as before, we now have a nice PowerShell object that we can derive orchestration decisions from by calling on the individual properties. $latestDeployment.status would allow us to focus in on the deployment status for decisions about whether or not to, say, continue on to a piece of orchestration that would close up the Master images given that patching has been completed.
Deployment Statuses
- Complete, All Patches Applied
- Complete, Some Patches Applied
- Download Complete Waiting
- Downloading
- Installing
- Not Applicable
- Waiting
- Error
Understanding this, one can write some relatively straightforward switch logic to define actions that would be appropriate to take depending on what the deployment status is in the context we’re discussing. You can do the same thing for the individual patch outcomes as shown below.
Tanium Patch Deployment Results
{xml}$deploymentResults = Get-Content -Path 'C:\Program Files (x86)\Tanium\Tanium Client\Patch\deployments\deployment-results' $resultObject = $deploymentResults.DeploymentResults.Deployment.Patch
The $resultObject is a custom object that has all the constituent details and status results for the individual patches. Executing $resultObject in your session will show you the following data:
That’s all for now. Cry havoc and let slip the dogs of automation.