I wanted to share a script I wrote a while back that will remotely install MSU patches. It requires PSTools to work and I ran this using Powershell 3.0.

This little method requires using two scripts, one that will install the MSU files locally, while the main script will execute the local installer remotely. It follows this process:

-The main script is ran and passed a path to where the MSU files are located at along with the secondary script, along with a textfile containing a list of computers.

The computer list should look like the following:
Computer1
CoolComputer252
ILikePancakes510

-The main script will robocopy all the files to the remote computer, this include the MSU patches and the secondary script.
-The main script subsequently runs PSExec on the secondary script which will install all the MSU patches under the system account.
-The secondary script then proceeds to taskkill itself to ensure the primary script starts installing patches on the next computer in the array.

It is also imperative that you have the proper credentials to remotely access the computer else this will fail.

Here is the first script.

Function Install_Patches([string]$path, [string]$computerList)
{
    Get-Content $computerList                                                     
    $arrayOfComputers = @()                                                  
    $arrayOfComputers = (Get-Content $computerList) -split "`n" 
    
    #Copy the files to each computer
    foreach($computer in $arrayOfComputers)
    {
        $OutputLocation = "\\" + $computer + "\C$\<LOCATION>"

        #path from the first paramater
        robocopy $path $OutputLocation /e /s
        Write-Host "Transfer completed for $computer"

        #patch the computer
        psexec $path -s cmd.exe /c "echo . | powershell.exe -executionpolicy bypass -file c:\<LOCATION>\CMDMSUInstall.ps1"
        Write-Host "Patching completed for $computer"
    }
}

Here is the second script:

cd C:\<LOCATION>
$path = "C:\<LOCATION>"
$files = Get-ChildItem $path -Recurse
$msus = $files | ? {$_.extension -eq ".msu"}

foreach($msu in $msus)
{
    $fullname = $msu.FullName
    $fullname = "`"" + $fullname + "`""
    $parameters = $fullname + " /quiet /norestart"
    $install = [System.Diagnostics.Process]::Start( "wusa",$parameters )
    $install.WaitForExit()
}
#kill itself to ensure it goes back to the next computer object
taskkill /f /im PSEXESVC.exe

Additonally, make sure you modify the location of where the patches will be copied too and where the local script is ran at.

Quick update, needed to add a bunch of new computer objects to a security group today. Figured I’d share this.

#Use one of the two below to get users.

#$arrObj = Get-ADComputer -Filter * -SearchBase "OU, DC, DC"

#$arrObj = Get-Content -Path <PathtoFile>

foreach($computer in $object)
{
    Get-ADComputer $computer | ForEach-Object {
        Add-ADGroupmember -identity 'Group Name' -members $_.SamAccountName
    }
}

I was given another task today to filter out some users in a certain fashion. A coworker asked me to get a few properties and one of the OUs certain users were residing in. Our structure is as follows OU1,OU2,OU3,user accounts. I was asked with the distinguished name to get the OU2 name and if the user object wasn’t residing in the same OU structure as provided, then to filter it out. I came up with the following code to query the results.

Get-ADUser -Filter * -SearchBase "OU=<OU1>,OU=<OU2>,OU=<OU3>,DC=<DC1>,DC=<DC2>,DC=<DC3>,DC=<DC4>" -Properties Name, personalTitle, telephoneNumber, MobilePhone, EmailAddress |
 Select-Object -property Name, personalTitle, telephoneNumber, MobilePhone, EmailAddress, @{label='DistinguishedName';expression={($_.DistinguishedName -replace'(.*.-?OU=Users,OU=)|(,OU.*)') }} |
 Where-Object {($_.DistinguishedName -notlike '*CN=*')}

The first part grabbed the users I wanted where the second line did the initial filter to retrieve just the second OU name with the regex portion stripping away most of the junk I didn’t need. The third line then removed any objects not following the same OU structure.

Work needed me to assist on creating a script for an SCCM package that would help automatically deploy MSIs. While this isn’t a ‘one script does all’ solution, I was able to provide them with a basic template to work with that they could tweak as needed for each package. This will provide the option to install/uninstall silently along with verbose install/uninstall.

Running this guy is pretty simple of course by passing the first argument with the type of installation, and the second with the MSI name.

Param([Parameter(Mandatory=$true,Position=1)]
        [ValidateSet('silentInstall','silentUninstall','verboseInstall', 'verboseUninstall')]
        [string]$InstallScriptParameters,
        [Parameter(Mandatory=$True,Position=2)]
        [string]$MSIFileName)
        

$OSArchitecture = (Get-WmiObject Win32_OperatingSystem).OSArchitecture
$localDir = [System.Environment]::CurrentDirectory.ToString()
$MSIFilePath = "$localDir\SourceFiles\$MSIFileName.msi"


If ($OSArchitecture -eq '64-bit')
    {
    $MSIExec = "$env:windir\SysWOW64\msiexec.exe"
    }
Else
    {
    $MSIExec = "$env:windir\System32\msiexec.exe"
    }



If ($InstallScriptParameters -like "silentInstall")
    {
        $params = @{
            "FilePath" = "$Env:SystemRoot\system32\msiexec.exe"
            "ArgumentList" = @(
                "/package"
                $MSIFilePath
                "/quiet"
            )
            "PassThru" = $true
        }
    }
ElseIf ($InstallScriptParameters -like "silentUninstall")
    {
        $params = @{
            "FilePath" = "$Env:SystemRoot\system32\msiexec.exe"
            "ArgumentList" = @(
                "/uninstall"
                $MSIFilePath
                "/quiet"
            )
            "PassThru" = $true
        }
    }
ElseIf($InstallScriptParameters -like "verboseInstall")
    {
        $params = @{
            "FilePath" = "$Env:SystemRoot\system32\msiexec.exe"
            "ArgumentList" = @(
                "/package"
                $MSIFilePath
            )
            "PassThru" = $true
        }
    }
ElseIf ($InstallScriptParameters -like "verboseUninstall")
    {
        $params = @{
            "FilePath" = "$Env:SystemRoot\system32\msiexec.exe"
            "ArgumentList" = @(
                "/uninstall"
                $MSIFilePath
            )
            "PassThru" = $true
        }
    }
Else 
    {
        Write-Host "No installation parameter was properly assigned."
    }       

Start-Process @params