PowerShell XML: Read and Write XML Files

Application configuration files, SOAP API responses, and exported data often arrive as XML — a format that PowerShell handles natively without any extra modules. The [xml] type accelerator loads an XML document as a .NET XmlDocument object whose structure you can navigate using dot notation, query with XPath, modify, and save back to disk. This post covers loading, navigating, querying, modifying, extending, and saving PowerShell XML files with practical examples you can adapt to your own configs and data files.
Load XML from File with [xml]
Cast a file path’s content to [xml] to load and parse the document. PowerShell creates a typed object tree where each element becomes an accessible property:
# Method 1: Cast Get-Content output
[xml]$config = Get-Content "C:\Config\app.config"
# Method 2: Load via XmlDocument for larger files (more efficient)
$xmlDoc = New-Object System.Xml.XmlDocument
$xmlDoc.Load("C:\Config\app.config")
# Both result in an XmlDocument — check the root element
Write-Host "Root element: $($config.DocumentElement.Name)"
Root element: configuration
Navigate XML as Object Properties
Child elements of an XML node are accessible as properties using dot notation. Attribute values are also accessible as properties:
# Given XML: <servers><server name="web01" ip="10.1.1.10"/></servers>
[xml]$data = @"
<servers>
<server name="web01" ip="10.1.1.10" role="web" />
<server name="db01" ip="10.1.1.20" role="db" />
</servers>
"@
# Navigate via dot notation
$data.servers.server # Returns all server nodes as array
$data.servers.server[0].name # "web01"
$data.servers.server[0].ip # "10.1.1.10"
# List all server names
$data.servers.server | ForEach-Object { Write-Host $_.name }
web01
db01
Select Nodes with SelectNodes
XPath expressions via SelectNodes and SelectSingleNode are more powerful than dot notation for complex queries across the document tree:
# Select all web servers
$webServers = $data.SelectNodes("//server[@role='web']")
$webServers | ForEach-Object { Write-Host "Web server: $($_.name) at $($_.ip)" }
# Select a single node
$db = $data.SelectSingleNode("//server[@role='db']")
Write-Host "DB server: $($db.name)"
# Select text content from an element
[xml]$appConfig = Get-Content "C:\Config\app.config"
$connString = $appConfig.SelectSingleNode("//connectionStrings/add[@name='Default']")?.connectionString
Modify XML Node Values
Assign directly to a property to update attribute values, or use InnerText to update element text content:
[xml]$config = Get-Content "C:\Config\app.config"
# Change an attribute value
$connNode = $config.SelectSingleNode("//add[@key='DatabaseServer']")
if ($connNode) {
$connNode.value = "ProdDB02"
Write-Host "Updated DatabaseServer to ProdDB02"
}
# Change element text content
$timeoutNode = $config.SelectSingleNode("//appSettings/add[@key='TimeoutSeconds']")
if ($timeoutNode) {
$timeoutNode.value = "60"
}
Add New XML Elements
Create new elements or attributes using the document’s CreateElement and CreateAttribute methods, then append them to the appropriate parent node:
[xml]$config = Get-Content "C:\Config\servers.xml"
$newServer = $config.CreateElement("server")
$newServer.SetAttribute("name", "cache01")
$newServer.SetAttribute("ip", "10.1.1.30")
$newServer.SetAttribute("role", "cache")
$serversNode = $config.SelectSingleNode("//servers")
$serversNode.AppendChild($newServer) | Out-Null
Write-Host "Added cache01 to servers list"
Write-Host "Total servers: $($config.servers.server.Count)"
Save Modified XML to File
Use the .Save() method to write the modified XML document to a file. This preserves the XML declaration (<?xml version="1.0"?>) and properly formats the output, unlike Set-Content which would just write the string representation:
[xml]$config = Get-Content "C:\Config\servers.xml"
# ... make modifications ...
# Save to the same file
$config.Save("C:\Config\servers.xml")
# Save to a new file (backup pattern)
$backupPath = "C:\Config\servers_$(Get-Date -Format 'yyyyMMdd_HHmmss').xml"
$config.Save($backupPath)
# Save with pretty-printing using XmlWriterSettings
$settings = New-Object System.Xml.XmlWriterSettings
$settings.Indent = $true
$settings.IndentChars = " "
$settings.OmitXmlDeclaration = $false
$writer = [System.Xml.XmlWriter]::Create("C:\Config\servers-formatted.xml", $settings)
$config.Save($writer)
$writer.Close()
Write-Host "XML saved"
Common Errors and Fixes
-
Namespace declarations require SelectSingleNode with namespace manager. XML documents with namespace declarations like
xmlns="http://..."require anXmlNamespaceManagerfor XPath queries. Without it,SelectSingleNode("//elementName")returns$nulleven when the element clearly exists. Create anXmlNamespaceManager, add the namespace prefix, and use it in your XPath:$ns.AddNamespace("x", "http://your.namespace.uri"); $config.SelectSingleNode("//x:element", $ns). -
Saving with .Save() not Set-Content to preserve XML declaration. Using
$config.OuterXml | Set-Content "file.xml"loses the XML declaration and may affect whitespace formatting. Always use$config.Save("path")for proper XML document writing.
Related Cmdlets / See Also
Wrapping Up
The [xml] type accelerator gives you full XML document manipulation in PowerShell without any external libraries. Use dot notation for simple navigation, XPath with SelectNodes/SelectSingleNode for complex queries, and always .Save() the document to disk rather than writing via Set-Content to preserve the XML structure and declaration.


