In Windows 10 und 11 sind viele Schriftarten bereits vorhanden. Oft hat eine Firma aber eine eigene Schriftart, welche für die Einhaltung von CI/CD auf allen Geräten benötigt wird. Dies kann z.B. "Open Sans" die "Deutschschweizer Basisschrift" oder eine andere Schriftart sein. In solchen Fällen nutze ich zum Installieren / Verteilen der Schriftarten via Intune ein PowerShell Script innerhalb eines Win32 Paketes.
Table of Contents
Win32 für Schriftarten
Das Win32 Packet baue ich wie die meisten meiner Pakete nach derselben Logik auf. Dies habe ich zuvor im Beitrag "my take on win32 apps - Intune" beschrieben.
In diesem Beispiel installiere ich die Schriftart Open Sans von Google, welche in vielen Webseiten zum Einsatz kommt. Zum Installieren weiterer Schriftarten via Intune musst du diese übrigens nur in den Ordner "Fonts" im Paket legen und das intunewin neu generieren.
Installation von Schriftarten
Um die Schriftart auf einem Endgerät zu installieren, müssen wir diese einerseits ins Verzeichnis "C:\Windows\Fonts" kopieren und zusätzlich in der Windows Registry unter "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" registrieren.
Da die meisten Schriftarten aus mehreren TTF oder OTF Dateien bestehen, habe ich das Paket so aufgebaut, dass du diese im Unterverzeichnis Fonts ablegen kannst. Pro TTF und/oder OTF Datei innerhalb dieses Ordners wird dann automatisch der Kopiervorgang inklusive Registry Key Erstellung durchgeführt.
Um das gleiche Paket mit allenfalls einem erweiterten Schriftsatz erneut installieren zu können, hinterlege ich im Validierungsfile übrigens eine Version.
$PackageName = "Company-Fonts"
$Version = "1"
$Path_local = "$Env:Programfiles\_MEM"
Start-Transcript -Path "$env:ProgramData\Microsoft\IntuneManagementExtension\Logs\$PackageName-install.log" -Force
try{
$WorkingPath = "$Path_local\Data\Fonts"
New-Item -ItemType "directory" -Path $WorkingPath -Force
Copy-Item -Path ".\Fonts\*" -Destination $WorkingPath -Recurse
$AllFonts = @()
$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-Error $_
}
}
Remove-Item $WorkingPath -Force -Recurse
New-Item -Path "$Path_local\Validation\$PackageName" -ItemType "file" -Force -Value $Version
}catch{
Write-Host "_____________________________________________________________________"
Write-Host "ERROR"
Write-Host "$_"
Write-Host "_____________________________________________________________________"
exit 1618
}
Stop-Transcript
Code language: PowerShell (powershell)
Deinstallation von Schriftarten
Bei der Deinstallation verwende ich im Grunde dieselbe Logik wie bei der Installation, einfach, dass hier im Gegensatz zu einem Kopiervorgang und der Registry Erstellung ein Löschvorgang ausgeführt wird. Ebenfalls lösche ich das Validierungsfile.
$PackageName = "Company-Fonts"
$Path_local = "$Env:Programfiles\_MEM"
Start-Transcript -Path "$env:ProgramData\Microsoft\IntuneManagementExtension\Logs\$PackageName-uninstall.log" -Force
$WorkingPath = "$Path_local\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_local\Validation\$PackageName" -ItemType "file" -Force -Value $Version
Stop-Transcript
Code language: PowerShell (powershell)
Erkennungsregel
Die Erkennungsregel ist sehr simpel und überprüft, ob das Validierungsfile mit der entsprechenden Zielversion als Inhalt vorhanden ist. Ist dies der Fall, wird "Found it!" ausgegeben und der Endpoint Manager beziehungsweise Intune weiss, dass da Paket auf dem Endgerät vorhanden ist.
$PackageName = "Company-Fonts"
$Version = "1"
$Path_local = "$Env:Programfiles\_MEM"
$ProgramVersion_current = Get-Content -Path "$Path_local\Validation\$PackageName"
if($ProgramVersion_current -eq $Version){
Write-Host "Found it!"
}
Code language: PowerShell (powershell)
Win32 Packet erstellen und verteilen
Mit dem Intune Win32 Prep Tool wird das Paket, nachdem das CSV und oder der Desktop Ordner abgefüllt ist erstellt:
Anschliessend erstellen wir ein "Windows app (Win32)" und laden die erstellte Datei «install.intunewin» im MEM/Intune mit den entsprechenden Parametern hoch. (rot zwingend, orange optional, aber hilfreich, wenn das Unternehmensportal genutzt wird.)
Name | Fonts oder individueller Name |
Beschreibung | Hinweis zu den Schriftarten |
Herausgeber | individuell |
Icon | frei wählbar, erscheint im Unternehmensportal |
Installations-Befehl | %SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -command .\install.ps1 |
Deinstallations-Befehl | %SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -command .\uninstall.ps1 |
Anforderungen | x64 und 2004 (Windows Version nicht relevant) |
Erkennungsregel | custom script, check.ps1 |
Abhängigkeit | keine |
Zuweisung | je nach Anforderung |
Nun hast du das Paket erstellt im Endpoint Manager hochgeladen und kannst es an die gewünschten Geräte verteilen. Vergiss bei einer Anpassung am Paket nicht, die Version sowohl im "install.ps1" als auch im "check.ps1" anzupassen.
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!
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: "$env:ProgramData\Microsoft\IntuneManagementExtension\Logs\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 log folder to program files (x86). So when the check ran, it said the installation failed since it couldn't find the log 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.
Hi, great tutorial, I have used it a lot 🙂 I have one problem, though. Uninstallation works, but Intune shows error: "The application was still detected after uninstallation completed successfully (0x87D1041D)".
In Fonts folder, the fonts are gone. But in C:\Program Files\_MEM\\Validationm the validation file Company-Fonts is still there, with latest version number.
Is there something I'm misunderstanding here?
Bumping my question of March 21 🙂
Hallo Florian,
vielen Dank für die gute Erklärung/das Tutorial. Ich bin den Schritten gefolgt. Leider bekomme ich jedoch die folgende Fehlermeldung "Failed 0x80070001". Ich bin etwas ratlos.
Hast du eine Idee?
Viele Grüße
Philipp
Hallo Philipp, ich nehme an, das ist der Fehler in Intune?
Kannst du mal prüfen, was das Log lokal auf dem Gerät sagt?
Das Log findest du im Pfad, der in der Zeile 5 definiert ist.
Thank you soo much after trying other scripts this one finally worked a charm
Never used Intune and tried this for the first time. Worked first time! Thanks a lot
Ich erhalte in Intune den Fehler "Die Anwendung wurde nach einer erfolgreichen Deinstallation nicht erkannt. (0x87D1041C)". Verteilt werden die Schriftarten aber.
Zwei Dinge sind mir aufgefallen, als ich das Skript lokal getestet habe:
1. $PackageName : The term '$PackageName' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again."
Das erklärt auch, wieso die Log-Datei bloß "-install.log" heißt statt "Company-Fonts-install.log".
2. Kann es sein, dass der Ordner "Validation" bereits existieren muss? Im besagten "-install.log" sehe ich ganz am Ende folgende Fehlermeldung: "ew-Item : Die Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch.
In C:\WINDOWS\IMECache\0ab97093-e23f-4939-8334-de41f85f4eb7_1\InstallFonts.ps1:28 Zeichen:5
+ New-Item -Path "$Path_local\Validation\$PackageName" -ItemType "f ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (C:\Program Files\_MEM\Validation\:String) [New-Item], IOException
+ FullyQualifiedErrorId : NewItemIOError,Microsoft.PowerShell.Commands.NewItemCommand
New-Item : Die Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch."
Deshalb wird wohl auch die Erkennung fehlschlagen, denn die Datei mit der Versionsnummer konnte ja gar nicht angelegt werden.
Kann es sein, dass in deinem "install.ps1" die erste Zeile fehlt?
$PackageName = "Company-Fonts"
Dort wird der Name definiert, welcher für das Log und auch die Validation verwendet wird.
Hallo Florian; ich möchte dir für die Zuverfügungsstellung deiner Skripts herzlich danken. Diese sind als Beginner sehr hilfreich und erleichtern die Integration erheblich! Weiter so! Merci velmol!
LG Simon Thomet
P.S. Falls du noch eine Idee für ein Skript suchst- ich möchte bei den Usern den Windows Bildschirmschoner mit eigenen Bilder zur Verfügung stellen- das wäre cool.