
Recently, I got asked by one of my customers if there is a way to extract information from Ivanti (Appsense) Environment Manger configuration that could be used somewhere else. This client has been using Ivanti UWM for years which accumulated large numbers of shortcuts creation actions, mapping actions and printer creation actions.
The customer also wanted to know the associated AD group condition upon which the user would get the action triggered. Example in the screenshot below.

Unfortunately, there is no built-in way in the console to export a certain node or action by type. So we had to improvise and find a way around that situation.
EM configuration file (*.aemp), is in reality a collection of XML files and one SDF file to track history changes. Unzip the file using your preferred method:

The XML file we are after is Configuration.xml which has all the actions, conditions, nodes, etc…

Looking inside the XML, the structure is far from readable and may vary based on how well the configuration was being maintained. I have seen configurations where lots of outdated, redundant and unnecessary actions where their let alone disabled actions that were left uncleaned.

Powershell to The Rescue!
We don’t know all the names of the nested properties in an XML file, however, with a quick search in the configuration we can see how Ivanti named the properties that we are after:
Drive Mapping Action: “UEM.Action.Drive.Map”
Printer Mapping Action: “UEM.Action.Printer.Map”
Shortcut Creation Action: “UEM.Action.Shortcut”
For conditions where the user is checked against an AD group membership, the property name is “UEM.Condition.UserGroupMembership”
Armed with this information, I wrote a recursive Powershell script that would scan all the nodes inside an XML files and evaluate them against the above names as required.
How Does It Work?
The script starts by importing the contents of the XML file into an XML object.
1 2 3 4 |
#Get the contents of EM config XML file [xml]$xml = Get-Content -path "$scriptPath\Configuration.xml" |
Then an empty array is created and the recursive function is called with the appropriate parameters
1 2 3 4 5 6 7 |
#Create an empty array $res = @() #Call the function and pass the variables as required $res += Recurse_EM_XML -LookedForProperty Shortcuts -XMLFile $XML |
So if the function is called with “Shortcuts” parameter the result will be something like this

Lastly, script will save the output into a CSV file placed in the same location as the script for analysis/use.
1 2 3 4 |
#Export the result into a CSV file $res | Export-Csv -Path $scriptPath\XML_Extract.csv -Force -NoTypeInformation |
Full code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
<# .Synopsis Script to extract the content of Ivanti EM XML configuration based on certain properties .DESCRIPTION The script takes in a XML file created by Ivanti Environment Manager and extract all actions and conditions upon which, Printers, Shortcuts, and Driver Mappings are being created to the user. .EXAMPLE Recurse_EM_XML -LookedForProperty Shortcuts -XMLFile $XML .EXAMPLE Recurse_EM_XML -LookedForProperty DriveMapping -XMLFile $XML .NOTES =========================================================================== Created on: 14/12/2020 Created by: Amir Joseph Sayes - Twitter @amirjsa Organization: Ninaronline.co.uk #> #Function to recurse through Appsense EM XML config and return shortcuts, printers, or mapped drives along with their AD group condition (if any) Function Recurse_EM_XML { [cmdletbinding()] Param ( [Parameter(Mandatory=$true)][object]$XMLFile, [Parameter(Mandatory=$true)][ValidateSet('DriveMapping','Shortcuts','PrinterMapping')][string]$LookedForProperty ) #check the identifier name, if matches, collect the AD group name if ($XMLFile.Identifier -eq "UEM.Condition.UserGroupMembership") { $ou = ($XMLFile.ActionProperties.ActionProperty | where {$_.name -eq "GroupName"} ).value } If ($LookedForProperty -eq 'Shortcuts') { #check the identifier name - if matches, collect the description and the enable/disable status if ($XMLFile.Identifier -eq "UEM.Action.Shortcut") { $description = $XMLFile.description.text $enabled = $XMLFile.enabled #Create the final object for output New-Object -Type PSObject -Property @{ Description = $description Path = ($XMLFile.ActionProperties.ActionProperty | where {$_.name -eq "TargetPath" }).value Parameters = ($XMLFile.ActionProperties.ActionProperty | where {$_.name -eq "Parameters" }).value OU = $OU Enabled = $Enabled } $description = $null $OU = $null } } If ($LookedForProperty -eq 'DriveMapping') { if ($XMLFile.Identifier -eq "UEM.Action.Drive.Map") { $description = $XMLFile.description.text $enabled = $XMLFile.enabled #Create the final object for output New-Object -Type PSObject -Property @{ Name = $description #Dive one more layer in to get more properties DriveLetter = ($XMLFile.ActionProperties.ActionProperty | where {$_.name -eq "DriveLetter" }).value RemotePath = ($XMLFile.ActionProperties.ActionProperty | where {$_.name -eq "RemotePath" }).value FriendlyName = ($XMLFile.ActionProperties.ActionProperty | where {$_.name -eq "FriendlyName" }).value OU = $OU Enabled = $Enabled } $description = $null $OU = $null } } If ($LookedForProperty -eq 'PrinterMapping') { if ($XMLFile.Identifier -eq "UEM.Action.Printer.Map") { $description = $XMLFile.name $enabled = $XMLFile.enabled #Create the final object for output New-Object -Type PSObject -Property @{ Name = $description Path = ($XMLFile.ActionProperties.ActionProperty | where {$_.name -eq "Path" }).value #Parameters = ($XMLFile.ActionProperties.ActionProperty | where {$_.name -eq "Parameters" }).value OU = $OU Enabled = $Enabled } $description = $null $OU = $null } } If ($XMLFile.HasChildNodes) { $Child_Nodes = $XMLFile.ChildNodes | Where {$_.NodeType -Eq "Element"} if ($Child_Nodes -ne $null) { ForEach ($Node In $Child_Nodes) { Recurse_EM_XML $Node -LookedForProperty $LookedForProperty } } } } #Main code #Gettting the location of the script if ($myInvocation.MyCommand.Path -ne $null) { $scriptPath = Split-Path $myInvocation.MyCommand.Path } else { $scriptPath = (Get-Location).Path} #If the script isn't saved, use the current location #Get the contents of EM config XML file [xml]$xml = Get-Content -path "$scriptPath\Configuration.xml" #Create an empty array $res = @() #Call the function and pass the variables as required $res += Recurse_EM_XML -LookedForProperty Shortcuts -XMLFile $XML #Spit out the results to the console $res #Export the result into a CSV file $res | Export-Csv -Path $scriptPath\XML_Extract.csv -Force -NoTypeInformation |
Thanks for the post! We’re having issues with drive mappings – our configuration has conditions (map drive for this group of users, but don’t map if username or usergroup is #value). It’s a real mess! Unfortunately your script doesn’t handle conditions like this 🙁