The PowerShell script discussed in this post allows you to deploy fonts within your Windows network.

Many of my clients work in creative industries, so a regular task for us is to install fonts. These fonts might be licensed on a per-machine basis or simply used for only one project and never seen again. This makes it difficult or essentially impractical to build a machine with all the required fonts already installed.

So when a new project starts, or more likely the last minute before several machines head out to give important presentations, you may need to install 50 fonts on 10 machines! But the users have no time for you to log on to their PCs and install the fonts.

Over the years, I have put together what we will generously call some "solutions" to this problem. The most recent was so labor intensive, it was probably more effort than that saved by walking to each individual computer with a pen drive containing the font files.

I speak of course of the Group Policy Preference (GPP) settings. Installing a font with a GPP is quite easy. You can set the font file to copy from a source folder to the C:\Windows\Fonts folder, and in the same GPP, set up the relevant registry key. After a reboot, the font is available for use.

GPP font example

GPP font example

GPP font example registry details

GPP font example registry details

However, as you can see from the two example images, managing multiple font installs using this method (having to create each registry item) is time consuming.

Then when asked to install several more fonts on several more computers, I thought again about using PowerShell. I had investigated font installs with PowerShell before, but I had never found a method that worked for me. I don't remember every example of what I tried, but I do recall one that tried to force the User Account Control (UAC) elevation prompt via PowerShell. The whole thing just seemed a bit lacking, so I put it on the back burner. However, soon I was back again, needing a quick way to install fonts without messing around.

I realized the answer was in front of me. I could use PowerShell to populate the registry section of the GPP for me! Genius.

No—wait. Why use GPP at all if PowerShell can do the registry entries for me? Goal!

So I present to you Font-Install.ps1. The script has two parameters: $pcNames and $fontFolder.

The $pcNames array will accept a list of PC Names, and $fontFolder (as the name suggests) is a string that should contain a Uniform Naming Convention (UNC) path to where the script can find the font files themselves.

The script will test a connection to each PC in turn. It will copy each font file it finds available on the network from the $fontFolder to the C:\Windows\Fonts folder on the PC using the admin $ share. It does this from the machine where the script executes to mitigate the issue of credential hopping through remote PowerShell sessions.

It then processes each font file for the font name (which is not always the same as the file name) and creates a registry entry for that font using Invoke-Command.

Font Install.ps1

Font Install.ps1

This is the full script:

Subscribe to 4sysops newsletter!

param(
[Parameter(Mandatory=$true,Position=0)]
[ValidateNotNull()]
[array]$pcNames,
[Parameter(Mandatory=$true,Position=1)]
[ValidateNotNull()]
[string]$fontFolder
)
$padVal = 20
$pcLabel = "Connecting To".PadRight($padVal," ")
$installLabel = "Installing Font".PadRight($padVal," ")
$errorLabel = "Computer Unavailable".PadRight($padVal," ")
$openType = "(Open Type)"
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
$objShell = New-Object -ComObject Shell.Application
if(!(Test-Path $fontFolder))
{
    Write-Warning "$fontFolder - Not Found"
}
else
{
    $objFolder = $objShell.namespace($fontFolder)
    foreach ($pcName in $pcNames)
    {
        Try{
            Write-Output "$pcLabel : $pcName"
            $null = Test-Connection $pcName -Count 1 -ErrorAction Stop
            $destination = "\\",$pcname,"\c$\Windows\Fonts" -join ""
            foreach ($file in $objFolder.items())
            {
                $fileType = $($objFolder.getDetailsOf($file, 2))
                if(($fileType -eq "OpenType font file") -or ($fileType -eq "TrueType font file"))
                {
                    $fontName = $($objFolder.getDetailsOf($File, 21))
                    $regKeyName = $fontName,$openType -join " "
                    $regKeyValue = $file.Name
                    Write-Output "$installLabel : $regKeyValue"
                    Copy-Item $file.Path  $destination
                    Invoke-Command -ComputerName $pcName -ScriptBlock { $null = New-ItemProperty -Path $args[0] -Name $args[1] -Value $args[2] -PropertyType String -Force } -ArgumentList $regPath,$regKeyname,$regKeyValue
                }
            }
        }
        catch{
            Write-Warning "$errorLabel : $pcName"
        }
    }
}