As part of a recent piece of work deploying a Modern Workplace solution for a customer, I was asked how could we deploy their corporate fonts to all machines so that corporate branding was maintained across documents that were produced in the organisation. Looking online there are several scripts that allow you to deploy an individual font (Deploying an embedded file (FONT) in a Powershell script through Intune MDM) however I had two font families with a total of 15 fonts that needed to be deployed. Encoding these files into Base64 would hit the limit of the PowerShell scripts that Intune Management Extension could execute so I had to look for an alternative.
The logical solution was to build an “application” that can deploy the fonts using the Win32app functionality in Intune and then push them as Required to the Intune managed computers. I would need several components for this:
- An Installer to copy the fonts into the right target location on the devices (whilst you can copy them to C:\Windows\Fonts there is a bit more configuration to complete the install)
- An Uninstaller to remove the fonts if they were no longer required (this is more for completeness and clean up in the future rather than a full requirement for the customer)
- The fonts in a logical format so they can be iteratively installed.
The install and uninstall process was quickly solved when I found this blog for Adding and Removing Fonts with Windows PowerShell. The script enables a single font, or all fonts in a particular folder to be installed. I made a tweak to complete this recursively over my install folder so that I could package all the fonts in a single application changing the relevant lines in the source script from this:
elseif ((Test-Path $path -PathType Container) -eq $true)
{
$bErrorOccured = $false
foreach ($file in (Get-Childitem $path)) {
if ($hashFontFileTypes.ContainsKey($file.Extension)) {
$retVal = Add-SingleFont (Join-Path $path $file.Name)
if ($retVal -ne 0) {
$bErrorOccured = $true
}
}
else {
"`'$(Join-Path $path $file.Name)`' not a valid font file type"
""
}
}
If ($bErrorOccured -eq $true) {
exit 1
}
else {
exit 0
}
}
to this
elseif ((Test-Path $path -PathType Container) -eq $true) {
$bErrorOccured = $false
write-host $path
foreach($file in (Get-Childitem $path -Recurse)) {
if ($hashFontFileTypes.ContainsKey($file.Extension)) {
$retVal = Add-SingleFont $($file.Fullname)
if ($retVal -ne 0) {
$bErrorOccured = $true
Write-Output "Install of $($file.name) Failed"
}
else { Write-Output "Install of $($file.name) Successful" }
}
else {
"`'$(Join-Path $path $file.Name)`' not a valid font file type"
""
}
}
If ($bErrorOccured -eq $true) { exit 1 }
else { exit 0 }
}
To make the install process easier I have created some single line scripts that will take all the files in the Fonts subfolder and call the Add-Font or Remove-Font script against them.
install.cmd
powershell.exe -executionpolicy bypass -command "& '.\Add-Font.ps1'" -args "-path .\Fonts"
uninstall.cmd
powershell.exe -executionpolicy bypass -command "foreach ($font in (Get-ChildItem -Path '.\Fonts' -Recurse)) { .\Remove-Font.ps1 -file $font.Name }"
Putting this all together I used the Intune Win32 app packager to create a file that I can load into Intune to deploy to my users.
Want to use this yourself? You can download the script components from the following link. All you need to do is add the font files into the Fonts subfolder, run the wrapping process and then upload to Intune. font-deployment
Hope you find this useful to further customise your Modern Workplace deployments
Thanks for this
During testing, I downloaded the script components from your site, added my fonts and then manually ran install.cmd as admin – which worked and fonts installed correctly
However, after I packaged the app via Intune app packager, uploading to Intune, the app installs without error from the Company Portal, yet the fonts are not placed inside Windows\Fonts folder
Have you got any ideas as to why ?
Can you confirm what the Install and Uninstall commands should be ? Should they be the .cmd files?
Keep running into:
ERROR System.ArgumentException: Illegal characters in path.
at System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional)
at System.IO.Path.Combine(String path1, String path2)
at Microsoft.Management.Service.IntuneWinAppUtil.Program.ReadInput(String description, String errorMessage, Func`2 validate, Func`2 updateValueWhenValidationFailed)
at Microsoft.Management.Service.IntuneWinAppUtil.Program.ReadInput(String[] args, String& sourceFolder, String& setupFile, String& outputFolder, String& catalogFolder)
at Microsoft.Management.Service.IntuneWinAppUtil.Program.Main(String[] args)
What happens if you run the install.cmd and uninstall.cmd files from the folder structure outside of the intunewin package?
Do you have any strange names in your font files?
Works Great, Many Thanks
Hello, I am pretty impressed by your work, however I am not sure to fully understand the whole process.
Wen you create the .intunewin file you put Add-Fonts as a setup file then everything (install.cmd, uninstall.cmd and actual Fonts from the Fonts folder are all wrapped up into the .intunewin file and your just have to upload it in Intune ?
I am not familiar with this process I just discovered it.
Hi Walter, the .intunewin creation process wraps the fonts, install cmd and install PS script together into a single file that can easily be downloaded and extracted to the machine. When the Intune services run locally they will extract the file and then execute the .cmd file which in turn calls the PowerShell script. The reason I execute it from a cmd script rather than the PowerShell directly is to ensure that the execution policy runs as expected.
Excelent tip.
One question: what setup file do you choose when you launch intunewin process (.cmd or .ps1)?
Thx
Hey, I call install.cmd and uninstall.cmd when putting the app into Intune as these files then call the PowerShell script in the correct way
How to deal with it? I have two files “install.cmd and uninstall.cmd”.
In this case in need 2x *.intunewin? One for installa and the other for uninstall?
All the files should be in a single .intunewin file that is uploaded to the Intune service.
When you call the install or uninstall process it will download and execute the code in the . intunewin file
i get this intune “install status:
The application was not detected after installation completed successfully (0x87D1041C)
further on, Windows 10 – events:
Anbieterintegrität: Failed to perform the CopyItem operation for the provider “FileSystem” in the path “C:\Windows\IMECache\*ID*_2\Fonts\*FONTNAME*.ttf”. The process cannot access the file “C:\Windows\Fonts\*FONT*.ttf”, because it is used by another process.
Details:
ProviderName=FileSystem
ExceptionClass=ProviderInvocationException
ErrorCategory=InvalidOperation
ErrorId=CopyItemProviderException
Severity=Warning
SequenceNumber=479
HostName=ConsoleHost
HostVersion=*VERSION*
HostId=*ID*
HostApplication=powershell.exe -executionpolicy bypass -command & ‘.\Add-Font.ps1’ -args -path .\Fonts
EngineVersion=*VERSION*
RunspaceId=*ID*
PipelineId=1
CommandName=
CommandType=
ScriptName=
CommandPath=
CommandLine=
Can you send me the files that are being added to the intunewin file so I can have a look at the error?
Hi,
As you described, I did the following:
“Want to use this yourself? You can download the script components from the following link. All you need to do is add the font files into the Fonts subfolder, run the wrapping process and then upload to Intune. font-deployment”
+
download this fonts:
https://fonts.google.com/specimen/Montserrat?selection.family=Montserrat
…=> unzip it and put all the *.ttf files into the font folder
It seems “Detection rules” are the problem… I have tried the following rules:
– Path: C:\Windows\Fonts
– File or folder: Montserrat
– Detection rule File or folder exists
second one – registry:
– Key path: Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts
– Value name: Montserrat Black (TrueType)
– Detection method: key exist
it doesn’t matter which rule is used, the same error occurs
i have local admin rules on the test device.
any sugesstions?
Let me run a test on one of my machines and will see what happens and let you know
Finaly it works!
Solution: instead of ‘value name’ i had to have use “valuename” => “” The key ist “”, if the output contains spaces
excuse the harassment
case closed,
Best knowHow transfer!
Glad it’s working, do I need to tweak my code in the article to cover this scenario?
detection rule has been requested here many times, in my case i use:
– Rule Type: Registry
– Registry: Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts
– Key path: “Montserrat Black (TrueType)”
– Detection Method: Value exists
There is no need to do changes on Code.
This is awesome. I’m trying to figure out a good detection rule in Intune for this deployment. I could test for the existence of one of the font files in the deployment in the C:Windows\Fonts folder, but I’m thinking that if someone had already installed that particular font, then my deployment of the entire set of fonts will be skipped. Anyone have any suggestions?
I had some similar thoughts and need to work out a better way to detect the font(s) that are being deployed. If you are only deploying one font it’s easy to check for the presence of that file but installing multiple does present a challenge
@mattywhi
Thank you for sharing this great deployment. After the Intune app packager is created and added to the Intune, what detection rules you used?
As a bit of a hack I check for one of the font files but as mentioned on other posts this isn’t always the best method and should be captured in some other format possibly writing something to the registry when all a particular font family is installed
what parameters have you set on the intune 32 app config page for install and removal?
I tried adding my fonts to the folder and then called the install script from an elevated powershell, however I do not see the fonts in C:\Windows\fonts despite getting a successful installation message for each
the install.cmd and uninstall.cmd are all I specify on the Program tab, it installs as System.
Will you be able to send a step by step process when you use the Intune Win32 App Packager for this procedure?
Thanks Matthew,
This really helped out one of our engineers with something he had been stuck on for a while.
Cheers!