Ivanti Environment Manager Configuration: How to export Shortcuts, Drive Mappings and Printer Mappings with Powershell

Ivanti Environment Manager Configuration: How to export Shortcuts, Drive Mappings and Printer Mappings with Powershell

December 14, 2020 1 By Amir Joseph Sayes

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.

#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

#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.

#Export the result into a CSV file 
 $res | Export-Csv -Path $scriptPath\XML_Extract.csv -Force -NoTypeInformation 

Full code

 <#
 .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