To import the XML file in PowerShell we use Import-Clixml CmdLet with the Path argument pointing to the location of the XML file.
IMPORTANT:ย Export-Clixmlย CmdLet exports XML in the exact format thatย Import-Clixmlย CmdLet needs and some sample XML files will not be imported unless in the proper format as I will show you in examples below. For such XML files use Get-Content CmdLet instead.
In my previous article How To Export XML File In PowerShell I have described the export part while in this part I will focus on the Import of XML file using PowerShell.
INFO: If you want to know how to read XML file using Get-Content CmdLet please refer to these sections of the previously mentioned export article. Read XML File Using Get-Content CmdLet and Read XML File And Convert It To XML Object In PowerShell.
Table of Contents
Import XML File Example Code
In this example we do several things:
- Collect properties of all Windows Services on the local machine using Get-Service CmdLet.
- Pipeline the result from Get-Service CmdLet to the Export-Clixml CmdLet that will save resultset as an XML file.
- Import XML file into $ImportXML variable using Import-Clixml CmdLet.
- Show the resultset in the PowerShell Grid.
Get-Service | Export-Clixml -Path 'C:\Temp\XMLExportDemo.xml' -Force
$ImportXML = Import-Clixml -Path 'C:\Temp\XMLExportDemo.xml'
$ImportXML | Out-GridView
Here is the resultset in the PowerShell grid.
Letโs quickly check the data type of the XML file using Get-Member CmdLet.
$ImportXML | Get-Member
Each object saved in the XML file is the Service Controller data type which is PowerShell data type for Windows Services. This is completely logical since we have collected the properties of Windows Services using Get-Service CmdLet prior to saving in the XML file and Get-Service CmdLet resultset data type is, of course, Service Controller data type.
If we look into the actual XML file we can see data type of collected objects and which properties are collected (Can Pause and Continue, Can Shutdown, Can Stop, Display Name, Dependencies, etc).
Import Failing For Sample XML File Into PowerShell
But what will happen if we try to import an XML file that was not exported using Export-Clixml CmdLet?
Here I have a sample XML file (XMLdemo.xml) used previously that is a valid XML document but it is not created by using Export-Clixml CmdLet.
Letโs try to import that XML file using Import-Clixml CmdLet.
$ImportXMLFailed = Import-Clixml -Path 'C:\Temp\XMLdemo.xml'
Import fails since our sample XML document is valid XML file but it is not formatted in the wayย Export-Clixmlย CmdLet does in PowerShell.
NOTE: Look at the tags of the two XML files and notice that sample XML file (XMLdemo.xml) is missing some tags created by Export-Clixml CmdLet(Objs, Obj, TN, etc.).
How to read such XML files and get the XML document in PowerShell to further manipulate it. Well, one of the solutions is to use Get-Content CmdLet instead of Import-Clixml CmdLet that is failing as we have seen.
Here is the sample code for that solution.
$Geography = Get-Content -Path 'C:\Temp\XMLdemo.xml'
$GeographyXML = [Xml] (Get-Content -Path 'C:\Temp\XMLdemo.xml')
The difference between the two calls is that first will as a result return String data type into PowerShell variable and later uses theย [XML] type accelerator and returns XML document data type as a result which is more convenient for working with XML documents since we get XML methods to do so.
To verify the data types of two variables above ($Geography and $GeographyXML) we can always use Get-Member PowerShell CmdLet and run the following lines of the code:
$Geography | Get-Member
$GeographyXML | Get-Member
The outcome and data types of both variables we can see in the links below, so no need to repeat ourselves here.
INFO: Please refer to the article How To Export XML File In PowerShell where I have explained these two examples in more depth in the sections: Read XML File Using Get-Content CmdLet and Read XML File And Convert It To XML Object In PowerShell.
How Did We Implement XML Files In Our Projects
Let me explain to you how we have implementedย Import-Clixmlย CmdLet in one of our CmdLets Compare-Versionย which is part of theย Efficiency Booster PowerShell Project. This project is a library of CmdLets that help us IT personal to accomplish our everyday IT task more efficiently and easily.
Before we dive into the code ofย Compare-Versionย CmdLet lets first explain the concept with an example. The idea for this CmdLet came as a part of the change management needs.
Imagine that you have some files and folders that you want to track their change over time (new, updated, deleted files and folders). One of the approaches to solve this would be:
- Collect all the relevant properties of files and folders (file version, file size, file and folder dates, etc.)
- Save collected data as an XML file that will represent a snapshot of the current state.
- The saved XML file is used as a reference point.
- Collect the filesโ and foldersโ properties and compare them against the reference snapshot XML data.
- Analyze the difference between current and reference states.
- After the analysis phase has been finished, archive the current reference XML file and take a new snapshot XML file as a new reference point.
Compare-Version CmdLet Explained
Of all the tasks named in the previous bulleted list, Compare-Version CmdLet does only the comparison between the baseline XML file and current state of the same properties collected in XML file while Save-Baseline CmdLet does collect of the relevant properties, save of collected data as an XML file using Export-Clixml CmdLet, Archives existing baseline XML file and creates new baseline XML file.
Save-Baseline CmdLet has been explained in my previous article How To Export XML File In PowerShell so please read there more about this CmdLet.
The tricky part with Compare-Version CmdLet is the actual call to the CmdLet that can be very often long and the interpretation of the result set can be confusing at the beginning until you get familiar with the logic but do not worry I will explain everything in small digestible chunks so everyone can hopefully understand it.
Here is first the example call to Compare-Version CmdLet:
Compare-Version -errorlog -Compare { Compare-Object -ReferenceObject ( Import-Clixml "$home\Documents\PSbaselines\Get-AllWindowsServices-OK-FIN.xml" ) -DifferenceObject (Get-AllWindowsServices -client "OK" -solution "FIN" -errorlog) -Property "Service name", Status, "Start mode", Started, "Start name", "Accept pause", "Accept stop" -PassThru | Sort-Object -Property "Service name", Collected | Select-Object SideIndicator, Environment, "Logical Name", "Server name", "Service name", "Process Id", "Path name", Status, Collected, "Start mode", Started, "Start name", "Accept pause", "Accept stop", Description, Caption, "Display name" } | Out-GridView
Compare-Version CmdLet uses in the Compare input parameter call to standard PowerShell CmdLet Compare-Object used for the comparison.
Compare-Object CmdLet has been passed values to three input parameters: ReferenceObject, DifferenceObject, and Property.
ReferenceObject input parameter has its own call to Import-Clixml CmdLet where we read previously saved XML file (baseline) and the XML has been saved using Save-Baseline CmdLet.
DifferenceObject input parameter has its own call to Get-AllWindowsServices (one of my own CmdLets) to read the Windows Service properties of the local machine which will be compared with the properties previously saved in the XML file.
Property input parameter defines a list of columns that we use to compare data between XML file properties and current state properties.
Resultset of Compare-Object CmdLet has been sent further down the PowerShell Pipeline using the PassThrue parameter to the Sort-Object CmdLet to sort the resultset.
Further resultset has been sent to Select-Object CmdLet where we decide which columns we want to show in the resultset.
And finally, for the presentation of the result, we use standard PowerShell Grid implementing Out-GridView CmdLet as the last CmdLet in this long PowerShell Pipeline.
Here are we finished with explaining the call to Compare-Version CmdLet. โAlles Klar!โ
So letโs focus now on the resultset of the Compare-Version CmdLet presented in the PowerShell Grid.
In order to understand the meaning of the results, we will focus on columns SiteIndicator and Service name.
AOC G-Menu Windows Service (marked green) has Side Indicator of value โ<=โ (marked orange) and that means it belongs to reference set. The reference set in this example is the values from the XML file which is our baseline.
Since we have only one instance of this Windows Service in the resultset and side indicator is from the XML file that means this Windows Service has been uninstalled from our system since the last save of the baseline.
Adobe Windows Service (marked green) has Side Indicator of value โ=>โ (marked orange) and that means it belongs to the difference set. The difference set in our example is the list of Windows Services that are currently on the local machine.
Since we have only one instance of this Windows Service in the resultset and side indicator is from the local machine current Windows Services list that means this Windows Service has been installed on our system since the last save of the baseline.
Finally, in our last result example, we have two lines in the result for the same Service Name (BITS). Side indicator โ<=โ row represents Windows Service properties from XML baseline file while Side indicator โ=>โ row represents Windows Service properties from the local machine.
This means that BITS Windows Service has been changed since last taking of the snapshot and in this example, in the column Process Id (marked red) have been different values.
Here is the table just as a quick reminder of Side Indicator explanation and meaning.
Side Indicator | Description | Number of rows |
---|---|---|
<= | Uninstalled, Deleted, Removed | 1 |
=> | Installed, New | 1 |
<= AND => | Edited, Updated | 2 |
Now when we understand the resultset we can do the analysis for change management purposes.
For example, we can have separate lists for the installed, uninstalled and updated Windows Service. Further, we can assess whether all installed Windows Services are necessary and according to our change procedures, the same thing for uninstalled and updated Windows Service.
After the assessment of changes has been finished we can archive the previous baseline and create a new baseline (XML file) and repeat the process whenever we need it in the future.
Compare-Version CmdLet โ Input Parameters
As input parameters we have:
- Compare โ input parameter with Script Block data type and it holds the syntax for comparing reference data from an XML file with actual data at that moment.
- errorlogย โ switch parameter that turns on/off logging of errors in an external text file
Function Compare-Version {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true,
HelpMessage="Path to XML baseline file name. Used as Reference object.")]
[ScriptBlock]$Compare,
[Parameter(HelpMessage="Write to error log file or not.")]
[switch]$errorlog
)
}
INFO: To know more aboutย PowerShell Parametersย andย Parameter Setsย with some awesome examples please read the following articlesย How To Create Parameters In PowerShellย andย How To Use Parameter Sets In PowerShell Functions.
BEGIN Block
The BEGINย block is empty.
PROCESS Block
Theย PROCESSย block is empty as well.
END Block
ENDย block is the main processing block for this CmdLet and in theย ENDย block we:
- Do the comparison using Invoke-Command CmdLet. Basically, runs the script block passed as input parameter Compare to the CmdLet.
- Error Handling using try-catch blocks and writing errors in an external text file using Write-ErrorLog CmdLet.
END {
try {
Write-Verbose "Begin compare between baseline and objects."
$obj = Invoke-Command -ScriptBlock $Compare -ErrorAction Stop
Write-Output $obj
Write-Verbose "Objects compared against baseline: $obj"
} catch {
Write-Warning "Compare-Version function failed"
Write-Warning "Error message: $_"
if ( $errorlog ) {
$errormsg = $_.ToString()
$exception = $_.Exception
$stacktrace = $_.ScriptStackTrace
$failingline = $_.InvocationInfo.Line
$positionmsg = $_.InvocationInfo.PositionMessage
$pscommandpath = $_.InvocationInfo.PSCommandPath
$failinglinenumber = $_.InvocationInfo.ScriptLineNumber
$scriptname = $_.InvocationInfo.ScriptName
Write-Verbose "Start writing to Error log."
Write-ErrorLog -hostname "Compare-Version has failed" -errormsg $errormsg -exception $exception -scriptname $scriptname -failinglinenumber $failinglinenumber -failingline $failingline -pscommandpath $pscommandpath -positionmsg $pscommandpath -stacktrace $stacktrace
Write-Verbose "Finish writing to Error log."
}
}
}
INFO: To understandย BEGIN,ย PROCESSย andย ENDย blocks in PowerShell please readย PowerShell Function Begin Process End Blocks Explained With Examples.
INFO: To learn about PowerShellย Error Handlingย and codeย debuggingย please read the following articles:ย How To Log PowerShell Errors And Much Moreย andย How To Debug PowerShell Scripts.
Comment-Based Help Section
For every one of my own CmdLets, I writeย Comment-Based helpย as well.
INFO: If you want to learn how to writeย comment-based Helpย for your own PowerShell Functions and Scripts please read these articlesย How To Write PowerShell Help (Step by Step). In this articleย How To Write PowerShell Functionโs Or CmdLetโs Help (Fast), I explain the PowerShell Add-on that helps us be fast with writing help content.
Compare-Versionย CmdLet Source Code
DISCLAIMER: Compare-Versionย function is part of theย Efficiency Booster PowerShell Projectย and as such utilize other CmdLets that are part of the same project. So the best option for you in order for this function to work without any additional customization is to download the source code of the whole project fromย here.
INFO: My best advice to every PowerShell scripter is to learn writing ownย PowerShell Advanced Functionsย andย CmdLetsย and I have written several articles explaining this, so please read them.ย How To Create A Custom PowerShell CmdLet (Step By Step). Here I explain how to use PowerShell Add-on Function to be faster in writing PowerShell Functionsย How To Write Advanced Functions Or CmdLets With PowerShell (Fast).
Here is the source code of the whole Compare-Versionย CmdLet:
<#
.SYNOPSIS
Compare between baseline in XML file and current state of objects.
.DESCRIPTION
Compare between baseline in XML file and current state of objects.
The Compare-Version cmdlet compares two sets of objects. One set of objects is the "reference set," and the other set is the "difference set."
reference set is xml file that we have created in ..\Documents\PSbaselines\ folder running Save-Baseline cmdlet and represents snapshot or baseline of objects properties at some point in time.
difference set is current state of objects property values.
Basically we compare snapshot taken in the past used as our baseline against current status and want to see the differance between them.
The result of the comparison indicates whether a property value appeared only in the object from the reference set (indicated by the <= symbol), only in the object from the difference set (indicated by the
=> symbol) or, if the IncludeEqual parameter is specified, in both objects (indicated by the == symbol).
Explanation - Result of comparation:
===============================================
Deleted objects
<=
New objects
=>
Updated objects
Equal objects
.PARAMETER Compare
Script block that will be passed as parameter and used for comparation.
.PARAMETER errorlog
write to log or not.
.EXAMPLE
Compare-Version -errorlog -Compare { Compare-Object -ReferenceObject ( Import-Clixml "$home\Documents\PSbaselines\Get-AllWindowsServices-OK-FIN.xml" ) -DifferenceObject (Get-AllWindowsServices -client "OK" -solution "FIN" -errorlog) -Property IP, "Service name", Status, "Start mode", Started, "Start name", "Accept pause", "Accept stop" -PassThru | Sort-Object -Property IP, "Service name", Collected | Select-Object SideIndicator, Environment, "Logical Name", "Server name", IP, "Service name", "Process Id", "Path name", Status, Collected, "Start mode", Started, "Start name", "Accept pause", "Accept stop", Description, Caption, "Display name" } | Out-GridView
Description
---------------------------------------
Compare diff of windows services on local server between baseline in XML file and current state of windows services.
.INPUTS
System.Management.Automation.PSCustomObject
InputObject parameter pipeline by value.
.OUTPUTS
System.Boolen
.NOTES
FunctionName : Compare-Version
Created by : Dejan Mladenovic
Date Coded : 10/31/2018 19:06:41
More info : http://improvescripting.com/
.LINK
How To Import XML File In PowerShell
Import-Clixml
Export-Clixml
Compare-Object
#>
Function Compare-Version {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true,
HelpMessage="Path to XML baseline file name. Used as Reference object.")]
[ScriptBlock]$Compare,
[Parameter(HelpMessage="Write to error log file or not.")]
[switch]$errorlog
)
BEGIN {
}
PROCESS {
}
END {
try {
Write-Verbose "Begin compare between baseline and objects."
$obj = Invoke-Command -ScriptBlock $Compare -ErrorAction Stop
Write-Output $obj
Write-Verbose "Objects compared against baseline: $obj"
} catch {
Write-Warning "Compare-Version function failed"
Write-Warning "Error message: $_"
if ( $errorlog ) {
$errormsg = $_.ToString()
$exception = $_.Exception
$stacktrace = $_.ScriptStackTrace
$failingline = $_.InvocationInfo.Line
$positionmsg = $_.InvocationInfo.PositionMessage
$pscommandpath = $_.InvocationInfo.PSCommandPath
$failinglinenumber = $_.InvocationInfo.ScriptLineNumber
$scriptname = $_.InvocationInfo.ScriptName
Write-Verbose "Start writing to Error log."
Write-ErrorLog -hostname "Compare-Version has failed" -errormsg $errormsg -exception $exception -scriptname $scriptname -failinglinenumber $failinglinenumber -failingline $failingline -pscommandpath $pscommandpath -positionmsg $pscommandpath -stacktrace $stacktrace
Write-Verbose "Finish writing to Error log."
}
}
}
}
#region Execution examples
#Differance from baseline
<#
Compare-Version -errorlog -Compare { Compare-Object -ReferenceObject ( Import-Clixml "$home\Documents\PSbaselines\Get-AllWindowsServices-OK-FIN.xml" ) -DifferenceObject (Get-AllWindowsServices -filename "OKFINservers.txt" -errorlog -client "OK" -solution "FIN") -Property IP, "Service name", Status, "Start mode", Started, "Start name", "Accept pause", "Accept stop" -PassThru | Sort-Object -Property IP, "Service name" | Select-Object * } | Save-ToExcel -sendemail -errorlog -ExcelFileName "DFB-GetAllWindowsServices" -title "Diff from baseline Get all windows services info of servers in Financial solution for " -author "DJ PowerScript" -WorkSheetName "DFB - All windows services" -client "OK" -solution "FIN"
Compare-Version -errorlog -Compare { Compare-Object -ReferenceObject ( Import-Clixml "$home\Documents\PSbaselines\Get-AllWindowsServices-OK-FIN.xml" ) -DifferenceObject (Get-AllWindowsServices -client "OK" -solution "FIN" -errorlog) -Property IP, "Service name", Status, "Start mode", Started, "Start name", "Accept pause", "Accept stop" -PassThru | Sort-Object -Property IP, "Service name" | Select-Object * } | Save-ToExcel -sendemail -errorlog -ExcelFileName "DFB-GetAllWindowsServices" -title "Diff from baseline Get all windows services info of servers in Financial solution for " -author "DJ PowerScript" -WorkSheetName "DFB - All windows services" -client "OK" -solution "FIN"
Compare-Version -errorlog -Compare { Compare-Object -ReferenceObject ( Import-Clixml "$home\Documents\PSbaselines\Get-AllWindowsServices-OK-FIN.xml" ) -DifferenceObject (Get-AllWindowsServices -client "OK" -solution "FIN" -errorlog) -Property IP, "Service name", Status, "Start mode", Started, "Start name", "Accept pause", "Accept stop" -PassThru | Sort-Object -Property IP, "Service name" | Select-Object * } | Out-GridView
Compare-Version -errorlog -Compare { Compare-Object -ReferenceObject ( Import-Clixml "$home\Documents\PSbaselines\Get-AllWindowsServices-OK-FIN.xml" ) -DifferenceObject (Get-AllWindowsServices -client "OK" -solution "FIN" -errorlog) -Property IP, "Service name", Status, "Start mode", Started, "Start name", "Accept pause", "Accept stop" -PassThru | Sort-Object -Property IP, "Service name", Collected | Select-Object SideIndicator, Environment, "Logical Name", "Server name", IP, "Service name", "Process Id", "Path name", Status, Collected, "Start mode", Started, "Start name", "Accept pause", "Accept stop", Description, Caption, "Display name" } | Out-GridView
Compare-Version -errorlog -Compare { Compare-Object -ReferenceObject ( Import-Clixml "$home\Documents\PSbaselines\Get-AllWindowsServices-OK-FIN.xml" ) -DifferenceObject (Get-AllWindowsServices -client "OK" -solution "FIN" -errorlog) -Property "Service name", Status, "Start mode", Started, "Start name", "Accept pause", "Accept stop" -PassThru | Sort-Object -Property "Service name", Collected | Select-Object SideIndicator, Environment, "Logical Name", "Server name", "Service name", "Process Id", "Path name", Status, Collected, "Start mode", Started, "Start name", "Accept pause", "Accept stop", Description, Caption, "Display name" } | Out-GridView
#>
#endregion
Useful PowerShell Articles About Import XML Data
Here are some useful articles and resources: