SCCM – Create New Site System Automatically

email me

Download: http://eddiejackson.net/apps/ConfigMgr2012PowerShellCmdlets.msi

Also see: SCCM – Automated WSUS & SUP Install

Save as: Create-NewSiteSystem.ps1

param (
[string]$SiteCode,
[string]$NewSUPServerName,
[string]$MPServer
)

Function Create-NewSystem {
$Hive = "LocalMachine"
$ServerName = "$($MPServer)"
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]$Hive,$ServerName,[Microsoft.Win32.RegistryView]::Registry64)
$Subkeys = $reg.OpenSubKey('SOFTWARE\Microsoft\SMS\Setup\')
$AdminConsoleDirectory = $Subkeys.GetValue('UI Installation Directory')
switch (Test-Path $AdminConsoleDirectory)
{
$true { Import-Module "$($AdminConsoleDirectory)\bin\ConfigurationManager.psd1" }
$false {
Write-Verbose "$($AdminConsoleDirectory) does not exist. Trying alternate path under ProgramFilesx86"
$AdminConsoleDirectory = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole"
Import-Module "$($AdminConsoleDirectory)\bin\ConfigurationManager.psd1"
}
}

Set-Location "$($SiteCode):"

if (-not (Get-CMSiteSystemServer -SiteSystemServerName $NewSUPServerName -SiteCode $SiteCode))
{
New-CMSiteSystemServer -ServerName $NewSUPServerName -SiteCode $SiteCode

if (-not (Get-CMSiteSystemServer -SiteSystemServerName $NewSUPServerName -SiteCode $SiteCode))
{
Write-Error "The Site System $($NewSUPServerName) has not been created. Please check the logs for further information"
exit 1
}
}
}

Create-NewSystem

SCCM – Sync SUP Automatically

email me

Download: http://eddiejackson.net/apps/ConfigMgr2012PowerShellCmdlets.msi

Also see: SCCM – Automated WSUS & SUP Install

Clear-Host

$SMSProvider = "SCCM.DOMAIN.COM"

 
Function Get-SiteCode
{
    $SMSQuery = “SELECT * FROM SMS_ProviderLocation”
    $SMSObject = Get-WmiObject -Query $SMSQuery -Namespace “root\sms” -ComputerName $SMSProvider
    $SMSObject | ForEach-Object {
    if($_.ProviderForLocalSite)
        {
            $SiteCode = $_.SiteCode
        }
}
 
return $SiteCode | Out-Null
}
 

Get-SiteCode
Write-Host "Provider:" $SMSProvider
Write-Host ""
Write-Host "Site Code Detected:" $SiteCode
Write-Host ""

 
$SUPRole = [wmiclass]("\\$SMSProvider\root\SMS\Site_$($SiteCode):SMS_SoftwareUpdate")
$SUPParams = $SUPRole.GetMethodParameters("SyncNow")
$SUPParams.fullSync = $true
$ReturnCode = $SUPRole.SyncNow($SUPParams)

Write-Host "Sync Return Code:" $ReturnCode.ReturnValue
Write-Host ""

 
if ($Return.ReturnValue -eq "0")
    {
        Write-Host "Sync was successful!"
    }
else
    {
        Write-Host "Sync failed!"
    }

# show sms info
Get-WmiObject -Query $SMSQuery -Namespace “root\sms” -ComputerName $SMSProvider

 

Output

__GENUS : 2
__CLASS : SMS_ProviderLocation
__SUPERCLASS :
__DYNASTY : SMS_ProviderLocation
__RELPATH : SMS_ProviderLocation.Machine=”SCCM.DOMAIN.com”,SiteCode=”001″
__PROPERTY_COUNT : 4
__DERIVATION : {}
__SERVER : SCCM
__NAMESPACE : ROOT\sms
__PATH : \\SCCM\ROOT\sms:SMS_ProviderLocation.Machine=”SCCM.DOMAIN.com”,S
iteCode=”001″
Machine : SCCM.DOMAIN.com
NamespacePath : \\SCCM.DOMAIN.com\root\sms\site_001
ProviderForLocalSite : True
SiteCode : 001
PSComputerName : SCCM

Detected Site Code: 001

__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ReturnValue : 0
PSComputerName :

Sync was successful!

SCCM – Automated WSUS & SUP Install

email me

Would you like to automate WSUS and SUP installation? This script can do it. Download the CmdLets and site creation script (save as Create-NewSiteSystem.ps1)…enter the info at the top, and watch it go.

Screenshots showing the automated setup

Also see: SCCM – Sync SUP Automatically

# Download http://eddiejackson.net/apps/ConfigMgr2012PowerShellCmdlets.msi

$SiteCode = "001"
$NewSUPServerName = "SQL.DOMAIN.COM"
$MPServer = "SCCM.DOMAIN.COM"
$DBServerName = "SQL"
$DBServerInstance = ""
$WSUSContentPath = "\\SCCM.DOMAIN.COM\Share\apps\updates"


Function Set-Property
{
    PARAM(
        $MPServer,
        $SiteCode,
        $PropertyName,
        $Value,
        $Value1,
        $Value2
    )

    $embeddedproperty_class = [wmiclass]""
    $embeddedproperty_class.psbase.Path = "\\$($MPServer)\ROOT\SMS\Site_$($SiteCode):SMS_EmbeddedProperty"
    $embeddedproperty = $embeddedproperty_class.createInstance()
    
    $embeddedproperty.PropertyName = $PropertyName
    $embeddedproperty.Value = $Value
    $embeddedproperty.Value1 = $Value1
    $embeddedproperty.Value2 = $Value2
    
    return $embeddedproperty
}

Function Create-NewSUP {

# Create Site

C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -file ".\Create-NewSiteSystem.ps1" -SiteCode $SiteCode -MPServer $MPServer -NewSUPServerName $NewSUPServerName

# connect to SMS Provider for Site
$role_class             = [wmiclass]""
$role_class.psbase.Path = "\\$($MPServer)\ROOT\SMS\Site_$($SiteCode):SMS_SCI_SysResUse"
$role                     = $role_class.createInstance()
#create the SMS Distribution Point Role
$role.NALPath     = "[`"Display=\\$NewSUPServerName\`"]MSWNET:[`"SMS_SITE=$SiteCode`"]\\$NewSUPServerName\"
$role.NALType     = "Windows NT Server"
$role.RoleName     = "SMS Software Update Point"
$role.SiteCode     = "$($SiteCode)"



$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "UseProxy" -value 0 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "ProxyName" -value 0 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "ProxyServerPort" -value 80 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "AnonymousProxyAccess" -value 1 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "UserName" -value 0 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "UseProxyForADR" -value 0 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "IsIntranet" -value 1 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "Enabled" -value 1 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "DBServerName" -value 0 -value1 '' -value2 '$($DBServerName\$DBServerInstance)')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "NLBVIP" -value 0 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "WSUSIISPort" -value 8530 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "WSUSIISSSLPort" -value 8531 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "SSLWSUS" -value 0 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "UseParentWSUS" -value 0 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "WSUSAccessAccount" -value 0 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "IsINF" -value 0 -value1 '' -value2 '')
$role.Props += [System.Management.ManagementBaseObject](Set-Property -MPServer $MPServer -sitecode $sitecode -PropertyName "PublicVIP" -value 0 -value1 '' -value2 '')

$role.Put()

}

Function Install-WSUS { 

if (-not (Get-WindowsFeature -Name UpdateServices).Installed -eq $true)
    {
        Install-WindowsFeature -Name UpdateServices-DB, UpdateServices-Ui -IncludeManagementTools -LogPath C:\Windows\System32\LogFiles\WSUSInstall.log
        $command = ". `"$env:ProgramFiles\Update Services\Tools\WsusUtil.exe`" PostInstall SQL_INSTANCE_NAME=$DBServerName\$DBServerInstance CONTENT_DIR=$WSUSContentPath"
        Invoke-Expression -Command $command 
        Write-Host "WSUS installed and configured!"
    }
else
    {
        Write-Host "WSUS is already installed!"
    }

}

# START

Install-WSUS

$SiteControlFile = Invoke-WmiMethod -Namespace "root\SMS\site_$SiteCode" -class "SMS_SiteControlFile" -name "GetSessionHandle" -ComputerName $MPServer

Invoke-WmiMethod -Namespace "root\SMS\site_$SiteCode" -class "SMS_SiteControlFile" -name "RefreshSCF" -ArgumentList $SiteCode -ComputerName $MPServer | Out-Null

Create-NewSUP 

Invoke-WmiMethod -Namespace "root\SMS\site_$SiteCode" -class "SMS_SiteControlFile" -name "CommitSCF" $SiteCode -ComputerName $MPServer | Out-Null

$SiteControlFile = Invoke-WmiMethod -Namespace "root\SMS\site_$SiteCode" -class "SMS_SiteControlFile" -name "ReleaseSessionHandle" -ArgumentList $SiteControlFile.SessionHandle -ComputerName $MPServer

# END

Restart the Whole WSUS/SUP Instance

email me

Some of my field notes while working on SCCM features and services…

Yes, there are going to be times you just have to start the Win Updates setup and config over again.

1) Uninstall SUP role

2. Uninstall WSUS

4. Delete SUSDB from SQL Server if any

5. Restart Server

6. Install WSUS (Choose to install DB not WID)

7. Configure Updates to be stored locally

8. Run Post Deployment Configuration from Server manager

9. Started WSUS Admin Console and ran Wizard (also choose to connect for the catalog sync) up to the Point where you select the products. Then, cancel wizard without choosing any products.

10. Install SUP role in SCCM and configure the right Settings (WSUS on Server 2012 Defaults to its own admin Website and ports 8530 and 8531) and select the products you require.

11. Check following log file if WSUS does not sync with SCCM.

Path: D:\Program Files\Microsoft Configuration Manager\Logs

WCM.log
wsyncmgr.log

12.) Start synchronization in the SCCM console

System.Runtime.InteropServices.COMException (0x80070003): The system cannot find the path specified.

email me

Some of my field notes while working on SCCM features and services…

Try the commands below in PowerShell…they will modify

C:\Windows\System32\ServerManager\ComponentConfiguration\UpdateServices-Services.xml, and add in the content location—-seen in bold.

<?xml version=”1.0″ encoding=”utf-16″?><INSTANCE CLASSNAME=”ServerComponent_UpdateServices_Services”><PROPERTY NAME=”ContentDirectory” TYPE=”string”><VALUE>\\FQDN.DOMAIN.com\Updates</VALUE></PROPERTY><PROPERTY NAME=”ContentLocal” TYPE=”boolean”><VALUE>true</VALUE></PROPERTY></INSTANCE>


Install-WindowsFeature -Name UpdateServices-Services,UpdateServices-DB –IncludeManagementTools

wsusutil.exe postinstall SQL_INSTANCE_NAME=”FQDN.DOMAIN.com\SCCM” CONTENT_DIR=D:\Updates

 

Something important to note, if IIS is not configured with the instance ID of 1, you may also receive invalid path.

Go to IIS manager and right click on the web site and select properties.

On the Web Site Tab click properties in the logging section.

At the bottom it shows you the log file name
It starts with W3SVCx where x is the instance ID. Make sure it contains 1.

 

Notes

wsusutil help postinstall

Missing or Cannot Start w3svc

email me

Some of my field notes while working on SCCM features and services…

If you’re having problems with the w3svc service

Try this first

Go to Task Manager > Processes and manually stop the W3SVC process. After doing this the process should start normally when restarting IIS

 

Try this second

Run > appwiz.cpl > Turn windows features on or off > Uncheck “Internet Information Services” and “Windows Process Activation Service”

Restart your machine.
Run > appwiz.cpl > install both “Internet Information Services” and “Windows Process Activation Service”

 

Then this…if it still doesn’t work

1. Confirm that “Windows Management Instrumentation” is started and its startup type is set to automatic.

2. Also make sure the following dependency services are started for World Wide Web Publishing Service:

Windows Process Activation Service
Remote Procedure Call (RPC)
DCOM Server Process Launcher
RPC Endpoint Mapper.

3. Open regedit, navigate to [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP]:

a) Double click on Start and change value data from 4(disabled) to 3(automatically).
b) Delete “NoRun” key if this key exists.

4. Uninstall “Internet information Service” and “Windows process activation service(if it is already installed)” from
“Turn windows feature on or off” and Restart your PC.

5. Type the below command in CMD and press enter:

net start http
Now it will notify you that service is already running.

6. Reinstall Internet information Service from “Turn windows feature on or off”.

Verify C:\Windows\System32\inetsrv\config\applicationHost.config looks correct. Check for a “system.web” section in this file which may be causing problems. Remove the section.

7. Make sure these 2 services running and their startup type is automatic.If they are disabled and not running right click on them and go to properties and change them.

Windows process activation service
Worldwide web publishing service

8. Start IIS and my websites are started now, no more “w3svc service is not running error.”

9. Restart computer.

 

Notes

netstat -a -o|findstr 80
sc config http start= auto
sc config http start = auto
net start w3svc

 

 

Assembly – Your First Program

email me

< back

Hey, people…Assembly (aka assembler, aka machine language, aka ASM) isn’t dead. I thought I’d write several programs demonstrating assembly language. So, expect more to follow after this one. Who knows, maybe I’ll even revisit some COBOLFortran, and Pascal—I still have all my books from college. Check out my prime numbers code…written in multiple languages: http://eddiejackson.net/wp/?p=15672


What is ASM?

An assembly language is a low-level programming language for microprocessors and other programmable devices. It is not just a single language, but rather a group of languages. An assembly language implements a symbolic representation of the machine code needed to program a given CPU architecture.

An assembly language is the most basic programming language available for any processor. With assembly language, a programmer works only with operations that are implemented directly on the physical CPU.

Assembly languages generally lack high-level conveniences such as variables and functions, and they are not portable between various families of processors. They have the same structures and set of commands as machine language, but allow a programmer to use names instead of numbers. This language is still useful for programmers when speed is necessary or when they need to carry out an operation that is not possible in high-level languages.

 

On to the code…

 

All my assembly coding and testing will be in Windows 10.

; I'd like to welcome you to assembly!
;
; This will be your first assembly program.
;
; Action items will include the hashtag symbol (#) 
; You're supposed to do something when you see it.
;
; #Download and install: http://eddiejackson.net/apps/masm32v11r.zip
;
; After successful installation, we can begin...
;
; The first thing you'll notice is that the semicolon is 
; being used for comments. Get used to seeing and using it.
; 
; Now, we begin coding our .ASM file...the main code block is highlighted below.
;
; #1 Open your favorite editor (could be notepad++), and begin typing this code. I do
; not recommend copying and pasting. Do the typing.
;
;
.386                                           ; Use the 386 instruction set
                                               
.model flat, stdcall                           ; Memory model - flat for Windows programs

option casemap :none                           ; Labels are case sensitive
include \masm32\include\windows.inc            ; Include files are required and add
include \masm32\include\kernel32.inc           ; functionality to your program   
include \masm32\include\masm32.inc             ; 
includelib \masm32\lib\kernel32.lib            ;
includelib \masm32\lib\masm32.lib              ;

.data                                          ; Initialized data follows this directive
 msg1 db "Assembly Message: ", 0               ; Variables go here
 msg2 db "Your first Assembly program!", 0 
   
.code                                          ; Starting point for your main code
start:                                         ; Code execution begins now
 invoke StdOut, addr msg1                      ; Calls the StdOut, passing addr of msg1
 invoke StdOut, addr msg2                      ; Calls the StdOut, passing addr of msg2
 invoke ExitProcess, 0                         ; Successful return code
end start                                      ; Code ends now
;
;
; #2 Save: this code as Test1.asm
; #3 Open: command prompt
; #4 To navigate to your file, type: cd PathTo\Test1.asm
; #5 To assemble your program, type: \masm32\bin\ml /c /Zd /coff Test1.asm
;
;
; OUTPUT
; Microsoft (R) Macro Assembler Version 6.14.8444
; Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

; Assembling: Test1.asm

; ***********
; ASCII build
; ***********
;
;
; #To link your program, type: \masm32\bin\Link /SUBSYSTEM:CONSOLE Test1.obj
; 
;
; OUTPUT
; Microsoft (R) Incremental Linker Version 5.12.8078
; Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
;
;
; #To run your program, type: Test1
;
;
; OUTPUT
; Assembly Message: Your first Assembly program!
;
;
; WOW! Look at you go. You'll be hacking the Matrix in no time.
;
;
; [ /options ]                                  [ /link linkoptions ]   
; /AT Enable tiny model (.COM file)             /nologo Suppress copyright message
; /Bl Use alternate linker                      /Sa Maximize source listing
; /c Assemble without linking                   /Sc Generate timings in listing
; /Cp Preserve case of user identifiers         /Sf Generate first pass listing
; /Cu Map all identifiers to upper case         /Sl Set line width
; /Cx Preserve case in publics, externs         /Sn Suppress symbol-table listing
; /coff generate COFF format object file        /Sp Set page length
; /D[=text] Define text macro                   /Ss Set subtitle
; /EP Output preprocessed listing to stdout     /St Set title
; /F  Set stack size (bytes)                    /Sx List false conditionals
; /Fe Name executable                           /Ta Assemble non-.ASM file
; /Fl[file] Generate listing                    /w Same as /W0 /WX
; /Fm[file] Generate map                        /WX Treat warnings as errors
; /Fo Name object file                          /W Set warning level
; /FPi Generate 80x87 emulator encoding         /X Ignore INCLUDE environment path
; /Fr[file] Generate limited browser info       /Zd Add line number debug info
; /FR[file] Generate full browser info          /Zf Make all symbols public
; /G<c|d|z> Use Pascal, C, or Stdcall calls /Zi Add symbolic debug info
; /H Set max external name length               /Zm Enable MASM 5.10 compatibility
; /I Add include path                           /Zp[n] Set structure alignment
; /link                                         /Zs Perform syntax check only

The assembly and linking can also be done in a GUI, in the Small Memory Footprint Editor.

Load up your ASM file.

BAM! You have assembled and linked your program. Now, from the console, just type test1, and your program will run.

 

 

Notes

Recommended Books

Modern X86 Assembly Language Programming: 32-bit, 64-bit, SSE, and AVX

X86 Assembly Language and C Fundamentals

If you’re going to do some serious debugging in Windows, download: WinDbg

PowerShell – Return List of Windows Updates from Microsoft

email me

This will return available Windows Updates

$WindowsUpdates = new-object -com "Microsoft.Update.Searcher"
$historyCount = $WindowsUpdates.GetTotalHistoryCount()
$updates = $WindowsUpdates.QueryHistory(0,$historyCount)
 
$Array =  @()
             
Foreach ($update in $updates)
    {
    $string = $update.title
 
    $Regex = "KB\d*"
    $KB = $string | Select-String -Pattern $regex | Select-Object { $_.Matches }
    $updateQueue = New-Object -TypeName PSobject
    $updateQueue | add-member NoteProperty "HotFixID" -value $KB.' $_.Matches '.Value
    #$updateQueue | add-member NoteProperty "Title" -value $string
    $Array += $updateQueue 
    }
 
$Array | 
Sort-Object HotFixID | 
Format-Table -AutoSize
Write-Host "$($Array.Count) updates found!"

#$Array | Sort-Object HotFixID -unique
#$results = $Array | Sort-Object HotFixID -unique
#$results | Out-File -FilePath C:\POWERSHELL\ReturnUpdates.txt

 

Screenshot

PowerShell – Excel Report for a Single Hotfix

email me

# KB to check 
$KB = "KB3176935"

# List of computers to check for KB
$computerList = 'C:\reports\computers.txt'

$setErrorAction = "SilentlyContinue"

# Application setup
$application = New-Object -comobject Excel.Application 
$application.visible = $True
$workbook = $application.Workbooks.Add() 
$addWorksheetItem = $workbook.Worksheets.Item(1)
$initialRow = 2

# Header Properties
$addWorksheetItem.Cells.Item(1,1) = "COMPUTER" 
$addWorksheetItem.Cells.Item(1,2) = "STATUS" 
$headerProperty = $addWorksheetItem.UsedRange
$headerProperty.Interior.ColorIndex = 0
$headerProperty.Font.ColorIndex = 0 
$headerProperty.Font.Bold = $True

# Cycle through computer list
Foreach ($computer in get-content $computerList) 
{     
    $addWorksheetItem.Cells.Item($initialRow,1) = $computer

    $PatchStatus = Get-WMIObject Win32_QuickFixEngineering -computer $computer | where {$_.HotFixID -eq $KB} 
        If($PatchStatus -eq $Null) 
            {   # KB not found
                $addWorksheetItem.Cells.Item($initialRow,2).Interior.ColorIndex = 0 
                $addWorksheetItem.Cells.Item($initialRow,2) = "FALSE" 
            } 
        Else 
            {   # KB found
                $addWorksheetItem.Cells.Item($initialRow,2).Interior.ColorIndex = 0 
                $addWorksheetItem.Cells.Item($initialRow,2) = "TRUE" 
            } 
    
            $initialRow = $initialRow + 1 
            }

    # Resize columns to fix
    $headerProperty.EntireColumn.AutoFit() 

 
 
Screenshot

PowerShell – Excel Report Multiple Hotfixes & Computers

email me

# Add hotfixes to scan for
$hotFixes = @(
    "KB973815", 
    "KB3176935", 
    "KB4019264",
    "KB3138901"
    )

# Add computers to scan for
$computers = (get-content -path c:\reports\computers.txt) 

# Report name
$Report = @()
$reportName = "c:\setup\Report_$(get-date -Uformat "%Y%m%d-%H%M%S").csv" 

# return hotfix
Function Get-HotFixStatus([string]$hotFixes, [string]$computers) 
{ 
  Get-WmiObject -class  win32_QuickFixEngineering -Filter "HotFixID = '$hotfixes'" -computername $computers
} 

# Output to screen - create report with true or false
Function Get-HotFixReport([string[]]$hotFixes, [string[]]$computers) 
{ 
  foreach($computer in $computers) 
  { 
    Write-Host " "
    Write-Host $computer
    foreach ($hotfix in $hotfixes) 
    { 
     Write-Host $hotfix 
     $status =  $(if(Get-HotFixStatus -hotfix $hotfix -computer $computer) {$true} else {$false}) 
     $object = New-Object -TypeName PSObject 
     $object | Add-Member -MemberType NoteProperty -Name Computer -Value $computer 
     $object | Add-Member -MemberType NoteProperty -Name HotFix -Value $hotfix 
     $object | Add-Member -MemberType NoteProperty -Name Installed -Value $status
     Write-host "--" $status
     $object      
    }
   } 
} 

Clear-Host
Write-Host "Compiling report..."
Get-HotFixReport -hotfix $hotfixes -computer $computers | 
Sort-object -property computer | 
Export-Csv $reportName -NoTypeInformation -UseCulture
ii $reportName

Write-Host " "
Write-Host "Done!"

 
 
Screenshot