Install fonts using Invoke-Command

This topic contains 2 replies, has 2 voices, and was last updated by  camelCreed 4 months ago.

  • Author
    Posts
  • #91719

    camelCreed
    Participant

    EDIT *** WORKING SOLUTION POSTED BELOW INITIAL QUESTION ***

    Hello,

    I think the issue with the script is my use of the -Scriptblock. It is confusing because the only variables I am using in the -Scriptblock are created there. They are not(?) local variables, but I get the error: You cannot call a method on a null-valued expression.

    Here is my script:

    #provide lab pc names
    $labPCs = @("pc1","pc2")
    
    #foreach lab $labPC, check for \tempFont directory. if false, create it and copy fonts. if true, copy new fonts to it
    ForEach ($pc in $labPCs) {
    
        #define $localFolder
        $localFolder = "\\$($pc)\c$\tempFont\"
    
        #test path to $localFolder, create and copy fonts if false, else copy fonts
        if (!(Test-Path -Path $localFolder)) {
    
            New-Item -ItemType Directory -Path $localFolder -Force
            Copy-Item -Path $newFonts -Destination $localFolder
    
        }
    
        Else {
        
        Copy-Item -Path $newFonts -Destination $localFolder
        
        }
        
    #for each font, check if it exists by name in the Fonts folder on the target machine. if not, attempt to install it...
        ForEach ($font in $newFonts) {
        
                if (!(Test-Path -Path ('\\'+$($pc)+'\C$\Windows\Fonts\'+$($font).Name))) {
    #start scriptblock
                    Invoke-Command -ComputerName $pc -ScriptBlock {
                    #create a new com object Shell.Application
                        $shell = New-Object -ComObject Shell.Application
                    #create fonts com object
                        $fontNS = $shell.NameSpace(0x14)
                    #grab newly copied fonts
                        $copiedFonts = Get-ChildItem "C:\tempFont\"
    #create com object from that directory
                        $copiedFontsNS = $shell.NameSpace($copiedFonts)
                    #use CopyHere method to copy items in $copiedFonts to Fonts namespace
                        $fontNS.CopyHere($copiedFontsNS.Items(), 0x14)
    
                        }
    
                }
        
        }
    
    }

    *** WORKING SOLUTION *** – my issues were a lack of understand of Invoke-Command, Scriptblock, and the asynchronous CopyHere method. I read up and understand these much better (so I stopped using CopyHere altogether :)), and now the script works great...

    #grab new fonts from share
    $newFonts = Get-ChildItem -Path C:\path\to\newFonts -Recurse -Include *.ttf,*.otf,*.fon
    
    #provide lab pc names
    $labPCs = @("pc1","pc2")
    
    #foreach lab $labPC, check for \tempFont directory. if false, create it and copy fonts. if true, copy new fonts to it
    ForEach ($pc in $labPCs) {    
    
        #define $localFolder
        $localFolder = "\\$($pc)\c$\tempFont\"
    
        #test path to $localFolder, create and copy fonts if false, else copy fonts
        if (!(Test-Path -Path $localFolder)) {
    
            New-Item -ItemType Directory -Path $localFolder -Force
            Copy-Item -Path $newFonts -Destination $localFolder
    
        }
    
        Else {
        
        Copy-Item -Path $newFonts -Destination $localFolder
        
        }
    
        #run the following commands on target lab PC's
        Invoke-Command -ComputerName $pc -ScriptBlock {
            
            #set dest path
            $destPath = "C:\Windows\Fonts\"
    
            #grab existing dest objects
            $destFiles = Get-ChildItem -Path $destPath -Recurse         
    
            #set new font source path
            $sourcePath = 'C:\tempFont\'
    
            #use this to grab all new font source objects
            $sourceFiles = Get-ChildItem -Path $sourcePath -Include *.ttf,*.otf,*.fon -Recurse
            
            #set directory for placing fonts to install
            $fontInstall = New-Item -Path $sourcePath -ItemType Directory -Name 'Install' -Force
    
            #set path to \Fonts in registry
            $regPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"      
    
            #for each source file, see if it exists in \Fonts on the lab PC... 
            ForEach ($s in $sourceFiles) {
    
                $regName = "$($s.Name)"
    
                #if not...
                if (-not(Test-Path ($destPath + $s.Name))) {
    
                    #copy to the Install directory...
                    Copy-Item -Path $s.FullName -Destination $fontInstall.FullName -Force;
    
                    #grab the full path to the font in it's new location...
                    $install = $fontInstall.FullName + '\' + $s.Name;
    
                    #for each font in the Install directory, copy it to \Fonts via shell object. This copy method is asynchronous...
                    #so I used a While loop with Start-Sleep to make sure the copy finishes before the script moves on...
                    #logic is 'While the font to install does not exist in \Fonts, sleep the script for 1 second until the asynch copy finishes...
                    ForEach ($i in $install) {
    
                        #copy the font file to the \Fonts folder
                        Copy-Item $i -Destination $destPath -Force
    
                        #register the newly copied fonts in the registry
                        New-ItemProperty -Path $regPath -Name $regName -Value $regName -Force | Out-Null;
    
                        }
    
                    #register the newly copied fonts in the registry
                    New-ItemProperty -Path $regPath -Name $regName -Value $regName -Force | Out-Null
                    
                    }                
    
                }
    
                #remove the Install folder from the lab PC.
                #Get-Item -Path $fontInstall.FullName | Remove-Item -Recurse -Force
    
            }
    
        }
    
    }
  • #91739

    postanote
    Participant

    Step through your code one segment at a time to make sure you are getting what you'd expect.
    Comment out that whole Invoke-Command segment, and put some other dummy code there and see what your results are.

    • #91787

      camelCreed
      Participant

      Hi there,

      Thank you for the comments.

      I have stepped through the code and everything works up to the scriptblock. If I do something like New-Item ... in the first line of the scriptblock, it works. The item is created on the remote computer.

      Thanks again!

You must be logged in to reply to this topic.