WPNinjas HeaderWPNinjas Header

Map printers with Microsoft Endpoint Manager

When moving to the cloud managed modern workplace based on Azure AD Join you will see that there are many benefits available. But especially designing the workplace can lead to problems as Microsoft Endpoint Manager currently supports nearly all policies and settings, but lacks the possibilities of GPO Preferences like drive mapping, file copy and connecting printer. 

All of them can simply be solved with a script or proactive remediation items. But all of them have the problem, that they are not executed during logon of a user and it can happen that it takes a while. Nicola Suter created a nice solution to map network drives. I created now a solution based on the same logic to copy files and connect printers which I share in this blog. 

My scripts provide the following benefits:

GPO Printer Mapping Import

You can use an existing GPO or create a new config directly in the PowerShell script. In this section I will show how you can migrate existing printer mappings from GPO Preferences.

First of all it’s important to export the current GPO’s as XML files.

Then you can use the following script to convert the xml to the PowerShell array which we require in the main script.

Add-Type -AssemblyName System.Windows.Forms
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{ 
    InitialDirectory = [Environment]::GetFolderPath('Desktop') 
    Filter = 'Policy XML (*.xml)|*.xml'
}
$null = $FileBrowser.ShowDialog()

$Policy = [xml](Get-Content -Path $FileBrowser.FileName)

#Not nice but works as Microsoft is randomly using different namespaces
$printers = @()
$printers += $Policy.GetElementsByTagName("q1:SharedPrinter")
$printers += $Policy.GetElementsByTagName("q2:SharedPrinter")
$printers += $Policy.GetElementsByTagName("q3:SharedPrinter")
$printers += $Policy.GetElementsByTagName("q4:SharedPrinter")
$printers += $Policy.GetElementsByTagName("q5:SharedPrinter")
$printers += $Policy.GetElementsByTagName("q6:SharedPrinter")
$printers += $Policy.GetElementsByTagName("q7:SharedPrinter")
$printers += $Policy.GetElementsByTagName("q8:SharedPrinter")
$printers += $Policy.GetElementsByTagName("q9:SharedPrinter")

foreach($printer in $printers){
    
    $path = $printer.Properties.path
    $default = $printer.Properties.default
    $name = $printer.name
    $ExcludeGroup =@()
    $IncludeGroup =@()
    if($null -ne $printer.Filters.FilterGroup){
        foreach($Filter in $printer.Filters){
            if($Filter.FilterGroup.not -eq 0){
                $IncludeGroup += $Filter.FilterGroup.name
            } else {
                $ExcludeGroup += $Filter.FilterGroup.name
                Write-Warning "$path | $($Filter.FilterGroup.name) | NOT"
            }
            if($Filter.FilterGroup.bool -ne "AND"){
                Write-Warning "$path | $($Filter.FilterGroup.name) | OR"
            }
            if($Filter.FilterGroup.userContext -ne 1){
                Write-Warning "$path | $($Filter.FilterGroup.name) | Device Context"
            }
        }
    }
    '$printers += [pscustomobject]@{PrinterName="'+$name+'";PrintServer="'+$path+'";ADGroup="'+($IncludeGroup -join ",")+'";Default="'+$default+'"}' | Out-File -FilePath "PrinterInfo.txt" -Append
    '$printers += [pscustomobject]@{PrinterName="'+$name+'";PrintServer="'+$path+'";ADGroup="'+($IncludeGroup -join ",")+'";Default="'+$default+'"}'
}

The executed script will looks as in the following screenshot:

Now you can just copy the output or locate the “PrinterInfo.txt” file in the current directory. Both locations contain an array with the printer information which is required in the next section.

Setup the script

Now we can use the main script to deploy the printers. The script will create a scheduled task when executed as SYSTEM and when not it will map the printers according to the memberships of the executing user.

To start the customization, open it and add the generated array items in the “Manual Variable Definition” section.

Now you can save the script or adjust the entries as required. 

Deploy Printer and Monitor

The customized script can now be uploaded to MEM Intune for deployment.

Select a name according to your naming convention and add a description if required.

Then upload the script and verify that it is executed as a SYSTEM account and in 64-Bit context.

Assign the script to the groups you wish to provide this functionality.

In the review screen you have the possibility to verify everithing and create the object in MEM.

You can now monitor the deployment as with any other script. On the endpoints you will find logs of executions in the temp folder of the user which logged on. 

Summary

I hope this solution helps when facing the same requirements as I had in my project. Improvements and feedback can easily be submitted via Github

Follow me
Latest posts by Thomas Kurth (see all)

7 Comments

Frank · February 20, 2022 at 22:32

Hi Thomas, that looks like a really great solution but when running the first script I get the error “Method invocation failed because [System.String] does not contain a method named ‘GetElementsByTagName'”. It looks like a .Net module is missing. Do you know how to get around that?
Thx, Frank

Thomas Kurth · February 22, 2022 at 09:12

Thank you! There was a missing statement due to WordPress which has removed it in the output. Now it should work

Willem · March 8, 2022 at 17:07

Hi Thomas, when using this script as described above no scheduled task is created on the end users device.
The log only shows:
Running as SYSTEM: True
TerminatingError(): “System error.”

If we execute the script manually with psexec, PowerShell shows:
Transcript started, output file is c:\windows\TEMP\PrinterMapping.log
Running as SYSTEM: True

But there is no task created and also the transcript keeps running.

On the same devices we can use the drive mapping script from Nicola Sluter without any issues.
Any idea on what’s going wrong here?
Thanks in advance!

    Thomas Kurth · April 27, 2022 at 23:00

    Just updated the script on Github with a fix

      Willem · May 2, 2022 at 11:00

      Thanks Thomas, issues are resolved now!

Dave · April 4, 2022 at 15:48

Getting the following error for every printer I specify.

Get-Printer : Cannot validate argument on parameter ‘ComputerName’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At line:151 char:54
+ … (Get-Printer -ComputerName ([URI]($Printer.PrintServer).host) -Name $ …

    Thomas Kurth · April 27, 2022 at 23:00

    Just updated the script on Github with a fix

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.