Back to top

FREE eBook: The Most USEFUL PowerShell CmdLets and more…

Grab a copy!

How To Export XML File In PowerShell

Using XML files is very common in the IT World and PowerShell gives us a possibility to manipulate XML files.

To export XML file in PowerShell we use Export-Clixml CmdLet likewise to import the XML file in PowerShell we use Import-Clixml CmdLet but in this article, we will focus on exporting XML files.

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 the examples below.

In the first part of this article, I will show you few examples working with XML data in PowerShell. In the second part, I will explain to you Save-Baseline one of my own CmdLets that I have used in my projects for making XML files as snapshots of different properties and for change management. In Save-Baseline CmdLet I have implemented Export-Clixml CmdLet.

Export/Import XML Examples

Here is the code with a few examples that we will use in this article to explain the subject:

$Geography = Get-Content -Path 'C:\Temp\XMLdemo.xml'

$GeographyXML = [Xml] (Get-Content -Path 'C:\Temp\XMLdemo.xml')

$GeographyXML | Export-Clixml -Path 'C:\Temp\XMLdemoExported.xml' -Force

$ImportXML = Import-Clixml -Path 'C:\Temp\XMLdemoExported.xml'

$ImportXMLFailed = Import-Clixml -Path 'C:\Temp\XMLdemo.xml'

Sample XML File Used In Examples

Here is the sample XML file used and as you can see it is a very simple example with few tags labeling Country name and their capital respectfully.

<geography>
<country>
<name>Norway</name>
<capital>Oslo</capital>
</country>
<country>
<name>Serbia</name>
<capital>Belgrade</capital>
</country>
<country>
<name>Croatia</name>
<capital>Zagreb</capital>
</country>
</geography>
Sample XML file (XMLdemo.xml)

Export/Import XML Examples – Explained

Let’s look at example code line by line and see what each example can do for us.

Read XML File Using Get-Content CmdLet

We can read sample XML file (XMLdemo.xml) using Get-Content CmdLet.

$Geography = Get-Content -Path 'C:\Temp\XMLdemo.xml'

It is very important to remember that while we read XML file using Get-Content CmdLet returning data type of this CmdLet is not XML data type but rather String data type as you can see on the following screenshot. So we do not have methods at our disposal to further manipulate XML document in XML fashion but rather usual strings’ manipulation methods.

INFO: Look example that follows to see how to read XML file using Get-Content CmdLet and get XML data type as a result.

We use Get-Member CmdLet to answer the question of returning data type from Get-Content CmdLet in this example.

$Geography | Get-Member
The string data type as a result of reading XML file using Get-Content CmdLet

Read XML File And Convert It To XML Object In PowerShell

In this example, we read the same sample XML file (XMLdemo.xml) as in the previous example using again Get-Content CmdLet but this time, in addition, we use the [XML] type accelerator in order to get XML data type as result and not String data type like in the previous example.

$GeographyXML = [Xml] (Get-Content -Path 'C:\Temp\XMLdemo.xml')

Let’s quickly check the data type of the Get-Content with [XML] type accelerator call using Get-Member CmdLet.

$GeographyXML | Get-Member

TIP: Please open the screenshot below in a separate tab in order to clearly see the result.

As you can see the resulting data type is XMLDocument and we have many methods at our disposal for manipulating the XML data (CreateAttribute, CreateNode, CreateXMLDeclaration, ImportNode, etc.).

The XML data type of reading the XML file using Get-Content CmdLet with [XML] type accelerator

Export XML Object Into XML File Using Export-Clixml CmdLet

XML Document created in the previous example can be pipelined to Export-Clixml CmdLet and as a result, we create an XML file in a format understandable by PowerShell Import-Clixml CmdLet.

$GeographyXML | Export-Clixml -Path 'C:\Temp\XMLdemoExported.xml' -Force

If we compare sample XML file (XMLdemo.xml) and the XML file (XMLdemoExported.xml) created by Export-Clixml with the same sample data we can notice the difference in formatting.

IMPORTANT: It is very important to remember that XML file exported with Export-Clixml can be imported with counterpart Import-Clixml CmdLet while the initial sample XML file cannot be imported. Both xml files are well-formated XML files but only first one is readable by PowerShell using Import-Clixml CmdLet .

Format of XMLdemoExported.xml file exported with Export-Clixml CmdLet

Notice the difference in format between the two XML files.

Format of sample XMLdemo.xml file

Import Well-Formated XML File Into PowerShell

Let’s import the XML file created in the previous example using Import-Clixml CmdLet and check the data type of imported data.

$ImportXML = Import-Clixml -Path 'C:\Temp\XMLdemoExported.xml'

As expected the data type of imported XML file is XML Document with all the methods useful to manipulate XML data.

IMPORTANT: As we have mentioned previously import was successful since the XML file has been created with Export-Clixml CmdLet.

$ImportXML | Get-Member

TIP: Please open the screenshot in a separate tab in order to clearly see the result.

The data type of imported XMLdemoExported.xml file using Import-Clixml CmdLet

Import Failing For Sample XML File Into PowerShell

In the last example, we want to import our sample XML document 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. We have seen the difference in the section Export XML object into XML file using Export-Clixml CmdLet

Basically, sample XML document (XMLdemo.xml) is missing some XML tags (Objs, and XD tags in this case).

Import of sample XML file fails using Import-Clixml CmdLet

How Did We Implement XML Files In Our Projects

Let me explain to you how we have implemented Export-Clixml CmdLet in one of our CmdLets Save-Baseline 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 tasks more efficiently and easily.

Before we dive into the code of Save-Baseline 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 at a certain point in time and compare them against the reference snapshot XML data that we previously took.
  • 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.

Save-Baseline CmdLet Explained

Of all the tasks named in the previous bulleted list, Save-Baseline CmdLet does the following: 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 while Compare-Version CmdLet that I will discuss in my next article does only the comparison between the baseline XML file and current state of the same properties collected in XML file.

Let’s look at a few calls to Save-Baseline CmdLet and their results.

In this call, we collect properties about Windows Services on the local machine using my own Get-AllWindowsServices and pipeline the result to Save-Baseline CmdLet.

Get-AllWindowsServices -client "OK" -solution "FIN" -errorlog | Save-Baseline -errorlog -BaselineFileName "Get-AllWindowsServices" -client "OK" -solution "FIN" -Verbose

As a result, the XML file has been created in the PSbaselines folder.

XML file created in PSbaselines folder

Here is the content of the XML file. This is a snapshot and reference point for future comparations.

XML collects properties about Windows Services on the local machine

Here is another call to Save-Baseline CmdLet that does some additional things compared with the previous call explained. The code does three things:

  • Collect data about windows services on local machine using our own Get-AllWindowsServices CmdLet and pipeline the result to our own Save-Baseline CmdLet.
  • Archive the previously created XML file in the Archive folder under the PSbaseline folder using the archive parameter.
  • Create a new XML file that will be used as a new snapshot and a reference point in future comparations.
Get-AllWindowsServices -client "OK" -solution "FIN" -errorlog | Save-Baseline -archive -errorlog -BaselineFileName "Get-AllWindowsServices" -client "OK" -solution "FIN" -Verbose

Here is an archived XML file with date-time stamp (yyyy-mm-dd_hh-mm-ss)

Archived XML file before creating new snapshot XML file.

INFO: In my next article I will write about second part of this serious and how to use Import-Clixml and Compare-Object CmdLets in my own Compare-Version CmdLet that is working together with Save-Baseline CmdLet. Compare-Version CmdLet is used to do the actual comparation between snapshot/reference XML file created by Save-Baseline CmdLet and current state of the same properties. So stay tuned.

UPDATE: Please read the article How To Import XML File In PowerShell where I have explained Compare-Version CmdLet in detail.

Save-Baseline CmdLet – Input Parameters

As input parameters we have:

  • InputObject – Objects sent down the PowerShell Pipeline.
  • BaselineFileName – Name of the XML file that represents a new baseline (snapshot of properties that we want to follow).
  • archive – switch parameter that archives current XML before making a new baseline XML file.
  • errorlog – switch parameter that turns on/off logging of errors occurring in the CmdLet as part of Error Handling in external text file
  • client – part of encapsulation and CSV file name (usually two letters abbreviation of your client).
  • solution – part of encapsulation and CSV file name (I use FIN for Financial and HR for Human Resource).

INFO: To get a deeper explanation about client and solution input parameters please read these two sections Parameter client, Parameter solution.

Function Save-Baseline {
[CmdletBinding()]
param (
    [Parameter(Mandatory=$true,
                ValueFromPipeline=$true, 
                HelpMessage="Input rows to be saved as baseline xml file.")] 
    [PSobject[]]$InputObject,
    
    [Parameter(Mandatory=$true, 
                HelpMessage="XML baseline file name.")]
    [string]$BaselineFileName,
    
    [Parameter(HelpMessage="Archive current baseline in Archive folder and create new baseline.")]
    [switch]$archive,
    
    [Parameter(HelpMessage="Write to error log file or not.")]
    [switch]$errorlog,
    
    [Parameter(Mandatory=$true, 
                HelpMessage="Client for example OK = O client, BK = B client")]
    [string]$client,
     
    [Parameter(Mandatory=$true,
                HelpMessage="Solution, for example FIN = Financial accounting, HR = Human Resource")]
    [string]$solution   
)
}

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

In the BEGIN block we:

  • create an empty array with $objects variable
BEGIN { 
    
    #Creat an empty array
    $objects = @()

}

PROCESS Block

In the PROCESS block we:

  • populate all the objects sent down the PowerShell Pipeline into the array created in the BEGIN block.
PROCESS { 

    $objects += $InputObject
    
}

END Block

END block is the main processing block for this CmdLet and in the END block we:

  • Create a Baseline folder if it doesn’t exist where XML files will be stored. (By default, it is PSbaselines folder in the user’s [My] Documents folder).
  • Archive the previous baseline (XML file snapshot) file before creating a new baseline XML snapshot file.
    • Create an Archive folder within the PSbaselines folder if it doesn’t exist.
    • Move the current baseline XML file to the archive folder and give the archive file date-time stamp.
  • Create a New Baseline XML snapshot file using Export-Clixml CmdLet.
  • Error Handling using try-catch blocks and writing errors in an external text file using Write-ErrorLog CmdLet.
END { 
    
    #Create PSbaselines folder if does not exist    
    if ( !( Test-Path -Path "$home\Documents\PSbaselines" -PathType "Container" ) ) {
        Write-Verbose "Create Baseline folder in: $home\Documents"
        New-Item -Name "PSbaselines" -Path "$home\Documents" -ItemType "Container" -ErrorAction Stop
        Write-Verbose "Baselines folder has been created."
    }
    
    $BaselineFile = "$home\Documents\PSbaselines\$BaselineFileName" + "-" + "$client" + "-" + "$solution" + ".xml"

    try {
        
        #Archive the baseline file in Archive folder before creating new baseline file.
        if ( $archive ) {
            
            #Create Archive folder if does not exist
            if ( !(Test-Path -Path "$home\Documents\PSbaselines\Archive" -PathType "Container" ) ) {
                Write-Verbose "Create archive folder in: $home\Documents\PSbaselines"
                New-Item -Name "Archive" -Path "$home\Documents\PSbaselines" -ItemType "Container" -ErrorAction Stop
                Write-Verbose "Archive folder has been created."
            }
            
            $date = Get-Date -UFormat "%Y-%m-%d_%H-%M-%S"
            $ArchiveFile = "$home\Documents\PSbaselines\Archive\$BaselineFileName" + "-" + "$client" + "-" + "$solution" + "-" + $date + ".xml"
            
            #This is used only for the very first time when neither PSbaselines nor Archive nor Baseline file exist.             
            if ( !( Test-Path -Path $BaselineFile -PathType "Leaf" ) ) {
                $objects | Export-Clixml -Path $BaselineFile -NoClobber -ErrorAction Stop
                Write-Verbose "Baseline file has been saved for very first time: $BaselineFile"
            }
            
            Write-Verbose "Archive baseline file: $BaselineFile"
            Move-Item -Path $BaselineFile -Destination $ArchiveFile -ErrorAction Stop
            Write-Verbose "Baseline file archived to: $ArchiveFile" 
        }    

        $objects | Export-Clixml -Path $BaselineFile -NoClobber -ErrorAction Stop
        
        Write-Verbose "Baseline file saved: $BaselineFile"
        
    } catch {
        Write-Warning "Save-Baseline 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 "Save-Baseline 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 BEGINPROCESS 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 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.

Save-Baseline CmdLet Source Code

DISCLAIMERSave-Baseline 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 Save-Baseline CmdLet:

<#
.SYNOPSIS
Save baseline xml file for differant comparation purposes and change tracking possibilities.
.DESCRIPTION
Save baseline xml file for differant comparation purposes and change tracking possibilities.
After baseline xml file has been created we can use other CmdLet to compare current state with baseline state. 
For example we can create baseline XML file with properties of files in some folder and than compare current properties of these files agains baseline in order to find updated, deleted or new files. 
.PARAMETER InputObject
Any object that will be saved as xml file.
.PARAMETER BaselineFileName
Prefix of xml file name. File name has client and solution parameter values. 
Xml file cannot be overwritten due to use of NoClobber parameter of Export-Clixml CmdLet
.PARAMETER archive
Switch parameter that archives current baseline in Archive folder and creates new baseline file.
.PARAMETER errorlog
write to log or not.
.PARAMETER client
OK - O client
BK - B client
.PARAMETER solution
FIN - Financial solution
HR - Humane Resource solution
.EXAMPLE
Get-FileProperties -inputobjects OKFINtestfiles.csv -errorlog -client "OK" -solution "FIN" -Verbose | Save-Baseline -BaselineFileName "TestClient" -errorlog -client "OK" -solution "FIN" -Verbose

Description
---------------------------------------
Output objects of Get-FileProperties CmdLet are input objects for Save-Baseline CmdLet.

.INPUTS
System.Management.Automation.PSCustomObject

InputObject parameter pipelines by value. 
.OUTPUTS
System.Boolen

.NOTES
FunctionName : Save-Baseline
Created by   : Dejan Mladenovic
Date Coded   : 10/31/2018 19:06:41
More info    : http://improvescripting.com/

.LINK 
How To Export XML File In PowerShell
Export-Clixml #> Function Save-Baseline { [CmdletBinding()] param ( [Parameter(Mandatory=$true, ValueFromPipeline=$true, HelpMessage="Input rows to be saved as baseline xml file.")] [PSobject[]]$InputObject, [Parameter(Mandatory=$true, HelpMessage="XML baseline file name.")] [string]$BaselineFileName, [Parameter(HelpMessage="Archive current baseline in Archive folder and create new baseline.")] [switch]$archive, [Parameter(HelpMessage="Write to error log file or not.")] [switch]$errorlog, [Parameter(Mandatory=$true, HelpMessage="Client for example OK = O client, BK = B client")] [string]$client, [Parameter(Mandatory=$true, HelpMessage="Solution, for example FIN = Financial accounting, HR = Human Resource")] [string]$solution ) BEGIN { #Creat an empty array $objects = @() } PROCESS { $objects += $InputObject } END { #Create PSbaselines folder if does not exist if ( !( Test-Path -Path "$home\Documents\PSbaselines" -PathType "Container" ) ) { Write-Verbose "Create Baseline folder in: $home\Documents" New-Item -Name "PSbaselines" -Path "$home\Documents" -ItemType "Container" -ErrorAction Stop Write-Verbose "Baselines folder has been created." } $BaselineFile = "$home\Documents\PSbaselines\$BaselineFileName" + "-" + "$client" + "-" + "$solution" + ".xml" try { #Archive the baseline file in Archive folder before creating new baseline file. if ( $archive ) { #Create Archive folder if does not exist if ( !(Test-Path -Path "$home\Documents\PSbaselines\Archive" -PathType "Container" ) ) { Write-Verbose "Create archive folder in: $home\Documents\PSbaselines" New-Item -Name "Archive" -Path "$home\Documents\PSbaselines" -ItemType "Container" -ErrorAction Stop Write-Verbose "Archive folder has been created." } $date = Get-Date -UFormat "%Y-%m-%d_%H-%M-%S" $ArchiveFile = "$home\Documents\PSbaselines\Archive\$BaselineFileName" + "-" + "$client" + "-" + "$solution" + "-" + $date + ".xml" #This is used only for the very first time when neither PSbaselines folder nor Archive nor Baseline file exist. if ( !( Test-Path -Path $BaselineFile -PathType "Leaf" ) ) { $objects | Export-Clixml -Path $BaselineFile -NoClobber -ErrorAction Stop Write-Verbose "Baseline file has been saved for very first time: $BaselineFile" } Write-Verbose "Archive baseline file: $BaselineFile" Move-Item -Path $BaselineFile -Destination $ArchiveFile -ErrorAction Stop Write-Verbose "Baseline file archived to: $ArchiveFile" } $objects | Export-Clixml -Path $BaselineFile -NoClobber -ErrorAction Stop Write-Verbose "Baseline file saved: $BaselineFile" } catch { Write-Warning "Save-Baseline 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 "Save-Baseline 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 #Get-FileProperties -inputobjects OKFINtestfiles.csv -errorlog -client "OK" -solution "FIN" -Verbose | Save-Baseline -BaselineFileName "TestClient" -errorlog -client "OK" -solution "FIN" -Verbose #Get-FileProperties -inputobjects OKFINcompareFilesSecEnv.csv -errorlog -client "OK" -solution "FIN" -Verbose | Save-Baseline -BaselineFileName "AppSecEnv" -errorlog -client "OK" -solution "FIN" -Verbose #Get-FileProperties -inputobjects OKFINcompareFilesProdEnv.csv -errorlog -client "OK" -solution "FIN" -Verbose | Save-Baseline -BaselineFileName "AppProdEnv" -errorlog -client "OK" -solution "FIN" -Verbose #Get-CPUInfo -filename "OKFINservers.txt" -errorlog -client "OK" -solution "FIN" -Verbose | Save-Baseline -errorlog -BaselineFileName "Get-CPUInfo" -client "OK" -solution "FIN" -Verbose #Get-CPUInfo -filename "OKFINservers.txt" -errorlog -client "OK" -solution "FIN" -Verbose | Save-Baseline -archive -errorlog -BaselineFileName "Get-CPUInfo" -client "OK" -solution "FIN" -Verbose #endregion

Useful PowerShell Articles About Export/Import XML Data

Here are some useful articles and resources:

About Dejan Mladenović

Post Author Dejan MladenovicHey Everyone! I hope that this article you read today has taken you from a place of frustration to a place of joy coding! Please let me know of anything you need for Windows PowerShell in the comments below that can help you achieve your goals!
I have 18+ years of experience in IT and you can check my Microsoft credentials. Transcript ID: 750479 and Access Code: DejanMladenovic
Credentials
About Me...

My Posts | Website

Dejan Mladenović

Hey Everyone! I hope that this article you read today has taken you from a place of frustration to a place of joy coding! Please let me know of anything you need for Windows PowerShell in the comments below that can help you achieve your goals! I have 18+ years of experience in IT and you can check my Microsoft credentials. Transcript ID: 750479 and Access Code: DejanMladenovic
Credentials About Me...

Recent Posts