Many fonts are already present in Windows 10 and 11. However, a company often has its own font, which is required for compliance with CI/CD on all devices. This can be, for example, "Open Sans", the "German-Swiss basic font" or another font. In such cases I use a PowerShell script within a Win32 package to install / distribute the fonts via Intune.
Table of Contents
Win32 for fonts
Like most of my packages, I build the Win32 package according to the same logic. I mentioned this earlier in the post "my take on win32 apps - Intune" described.
In this example I am installing the font OpenSans by Google, which is used in many websites. Incidentally, to install additional fonts via Intune, you only have to place them in the "Fonts" folder in the package and regenerate the intunewin.
Installing fonts
To install the font on a device, we have to copy it to the "C:\Windows\Fonts" directory and also register it in the Windows Registry under "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts".
Since most fonts consist of several TTF or OTF files, I have structured the package so that you can store them in the Fonts subdirectory. The copying process, including the creation of a registry key, is then carried out automatically for each TTF and/or OTF file within this folder.
Incidentally, in order to be able to reinstall the same package with an extended font if necessary, I store a version in the validation file.
$PackageName = "Company-Fonts"
$Version = "1"
$Path_4netIntune = "$Env:Programfiles\4net\EndpointManager"
Start-Transcript -Path "$Path_4netIntune\Log\$PackageName-install.log" -Force
$WorkingPath = "$Path_4netIntune\Data\Fonts"
New-Item -ItemType "directory" -Path $WorkingPath -Force
Copy-Item -Path ".\Fonts\*" -Destination $WorkingPath -Recurse
$AllFonts = Get-ChildItem -Path "$WorkingPath\*.ttf"
$AllFonts += Get-ChildItem -Path "$WorkingPath\*.otf"
foreach($FontFile in $AllFonts){
try{
Copy-Item -Path "$WorkingPath\$($FontFile.Name)" -Destination "$env:windir\Fonts" -Force -PassThru -ErrorAction Stop
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" -Name $FontFile.Name -PropertyType String -Value $FontFile.Name -Force
}catch{
Write-Host $_
}
}
Remove-Item $WorkingPath -Force -Recurse
New-Item -Path "$Path_4netIntune\Validation\$PackageName" -ItemType "file" -Force -Value $Version
Stop-Transcript
Code language: PowerShell (powershell)
Uninstalling fonts
When uninstalling, I basically use the same logic as installing, simply that here a delete process is performed as opposed to a copy process and registry creation. I also delete the validation file.
$PackageName = "Company-Fonts"
$Path_4netIntune = "$Env:Programfiles\4net\EndpointManager"
Start-Transcript -Path "$Path_4netIntune\Log\uninstall\$PackageName-install.log" -Force
$WorkingPath = "$Path_4netIntune\Data\Fonts"
New-Item -ItemType "directory" -Path $WorkingPath -Force
Copy-Item -Path ".\Fonts\*" -Destination $WorkingPath -Recurse
$AllFonts = Get-ChildItem -Path "$WorkingPath\*.ttf"
$AllFonts += Get-ChildItem -Path "$WorkingPath\*.otf"
foreach($FontFile in $AllFonts){
try{
Remove-Item -Path "$WorkingPath\$($FontFile.Name)" -Force
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" -Name $FontFile.Name -Force
}catch{
Write-Host $_
}
}
Remove-Item $WorkingPath -Force -Recurse
Remove-Item -Path "$Path_4netIntune\Validation\$PackageName" -ItemType "file" -Force -Value $Version
Stop-Transcript
Code language: PowerShell (powershell)
Detection rule
The recognition rule is very simple and checks whether the validation file is available with the corresponding target version as content. If so, "Found it!" output and the Endpoint Manager or Intune knows that the package is available on the end device.
$PackageName = "Company-Fonts"
$Version = "1"
$ProgramVersion_current = Get-Content -Path "$Env:Programfiles\4net\EndpointManager\Validation\$PackageName"
if($ProgramVersion_current -eq $Version){
Write-Host "Found it!"
}
Code language: PowerShell (powershell)
Create and distribute Win32 package
With the Intune Win32 Prep Tool the package is created after the CSV and/or the desktop folder is filled:

We then create a "Windows app (Win32)" and upload the created «install.intunewin» file to MEM/Intune with the appropriate parameters. (Red mandatory, orange optional, but helpful when using the company portal.)
Name | Fonts or custom name |
Description | Note on the fonts |
editor | individually |
icon | freely selectable, appears in the company portal |
installation command | %SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -command .\install.ps1 |
uninstall command | %SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -command .\uninstall.ps1 |
conditions | x64 and 2004 (Windows version not relevant) |
Detection rule | custom script, check.ps1 |
dependency | none |
allocation | depending on requirements |
Now you have created the package, uploaded it to the Endpoint Manager and can distribute it to the desired devices. When making changes to the package, don't forget to change the version in both "install.ps1" and "check.ps1".
Thanks for this, only image "image-21.png" is hyperlinked - the other thumbnails don't link to their full-size equivalent, but they can be opened via
https://scloud.work/wp-content/uploads/2022/02/image-23.png for example
My fault, just added the media link to all the others. Thanks for noticing!
Pingback: Normal.dotm with Intune (Word template) | scloud
Keep up the good work! Looks like a perfect solution for my needs, but keep running in to a problem.
I have 18 fonts in the subfolder but only 1 gets installed and I get the following log error:
Method invocation failed because [System.IO.FileInfo] does not contain a method named 'op_Addition'.
At C:\WINDOWS\IMECache\698de435-f476-4e88-9b8a-485b822faa63_1\install.ps1:12 char:1
+ $AllFonts += Get-ChildItem -Path "$WorkingPath\*.otf"
Not a coder so can't decipher how I should correct the install.ps1 to correct this, any chance you know of a fix to fix this error?
Thanks!
Could be because of the Array "$AllFonts". Did you have only one ".tff" file and multiple ".oft"?
Anyway, I've made a small update thanks to your feedback.
Could you test it again with the newest install.ps1 from my repo?
Hi,
Yes, I have mutiple .otf files and one .tff.
Yes, I have tried the updated install.ps1 and now i works correctly! Thank you so much for quick response and fix! Works wonderfull!
I get an error when trying to run the install.ps1 "Error the setup file you specified can not be accessed"
Can you check your "install command"?
Or it can help to use the newest Intune prep Tool: https://github.com/Microsoft/Microsoft-Win32-Content-Prep-Tool
Hi Florian,
Thanks for sharing this. I have deployed this as per your demonstration and within EPM I can see successful install status. I checked that the fonts were within C:\Windows\Fonts and have also checked within office. The fonts have not installed. Any ideas?
Thanks.
Hi Daniel, can you send me the log and your package in a link via the contact form?
Log location: "C:\Program Files\4net\EndpointManager\Log\Company-Fonts-install.log"
Hi Florian,
I have found a resolution. I decided to create a ps1 and ran it from my machine and found that the ttf fonts applied, but the otf hadn't. On the back of this, I converted the otf fonts to ttf, recreated as per your demo and then re-deployed. Success 🙂
Thanks very much for this, it's been really helpful and thanks for replying 🙂
Side note- (Kind of cheeky) Do you have a similar routine for deploying fonts into Outlook 365 - using mailsettings in reg?
I have created a ps script using a combo of notepad++ and taking the binary from the mailsettings (in regedit) which works if I run locally, but it fails miserabley- when deployed in InTune using Scripts.
Haven't had the problem with OFT's before. But thanks for the hint, may this help someone else 🙂
Regarding Outlook 365, no haven't had this challenge so far.
Note that if the 'try' fails, your script still adds the validation showing it installed.
On my script I changed the catch to the following so that if the try section fails, it will not create the validation file:
}catch{
Write-Host $_
Stop-Transcript
Exit 1618
}
Good point, thanks.
Added the lines as well.
I used your example with the Open-Sans font and it's working and I can see the font in the C:\Windows\Fonts folder. I have 2 other fonts that I would like to install via Endpoint Manager/Intune. In Intune they are installed, but I can't find them in the C:\Windows\Fonts folder.
Do you have any idea?
Many thanks.
Great and helpfull script! 🙂
I had that issue once; can you try to make the file-ending (.OTF or .TTF) in all capital?
I will try that today and I will post an update here later!
Thanks.
It's still not working properly. The application is installed, but the font is still missing in the Fonts folder (C:\Windows\Fonts).
It's working now!
hi there,
i am having same issue.
it says install but there is no fonts in c drive fonts folder.
how do you solved this issue ?
I followed your directions and on two machines everything was successful, however on a few other machines I see the install failed. Within Intune I see "Access is denied. (0x80070005)". Upon investigating further it looks like Cylance blocked the Install.ps1 script.
Any thoughts why I might be receiving this error?
Hi David, I don't know Cylance.
Did you ever have problems with blocking win32 installations before? Perhaps you can whitelist the Intune process
Florian, Thank you for your response. Cylance is our Ant-virus, mal-ware, and script protection. No I have never had an Intune script blocked before. I'm wondering if the script is getting blocked because it's calling powershell from a specific location? I do have "\Program Files (x86)\Microsoft Intune Management Extension" whitelisted, thus never have had this issue. I tried to use .\install.ps1 for the install command, but it never installed. I assume this all needs to be done via powershell and no way to install via batch file?
Thank you for any help you can give me.
Florian,
Great news I've figured out the issue. The first issue is that Cylance has script control block except when powershell is run from within a particular folder. Since the install path called powershell in a folder which isn't whitelisted it was blocked. The second issue I ran into was the install script for some reason install the 4net folder to program files (x86). So when the check ran, it said the installation failed since it couldn't find the 4net folder. To overcome this, I generated a registry key, which I can use for detection.
If interested, here's the install command I used. powershell -executionpolicy bypass -file install.ps1.
I do want to thank you for sharing the scripts and your knowledge with installing font. Without the scripts you wrote, I would have never gottn this far.
Thank you,
David
Hi David, thanks for your feedback and the clarification. Happy it works now!
FYI: your command and the provided from my side do the same, except that mine calls PowerShell in x64 context. It's not necessary in this scenario, but I do it in all my packages, just in case.
Florian,
Thank you for your script. It does work with new fonts, but i need to overwrite existing fonts as well, such as the Segoe fonts. Most of them fail with the following error below:
PS>TerminatingError(Copy-Item): "The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Access to the path 'C:\Windows\Fonts\segoepr.ttf' is denied."
Access to the path 'C:\Windows\Fonts\segoepr.ttf' is denied.
PS>TerminatingError(Copy-Item): "The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Access to the path 'C:\Windows\Fonts\segoeprb.ttf' is denied."
Any way we can get past this? I have an application that when installed via intune, has issues displaying text. The fix is to overwrite the segoe fonts from a working pc. Works fine if i do it manually, but would like to script it and push out via intune.
All help is much appreciated!
Hi Joe,
I think cause this is the system font, it's in use.
Perhaps the best way would be to change the default system font in the registry.
Here a brief description for that: https://www.hexnode.com/mobile-device-management/help/script-to-change-fonts-on-windows-10-devices/
Let me know if this helps and solves your problem.