SCCM – Managing Google Chrome

With Microsoft’s Edge browser not living up to expectations and Internet Explorer being what we all remember (compatibility issues anyone), most businesses have a requirement to run a third-party browser such as Google Chrome or Mozilla Firefox.

Chrome is usually the popular choice for this as most people are familiar with it, and there are Group Policy ADMX templates available.

Unfortunately, Chrome presents an issue for many businesses, with automatic updates, corrupt installations, and inaccurate version numbers being common-place.

In this post, I will cover how to get from a relatively unmanaged Chrome environment, so one that you can easily control through SCCM

Understanding Chrome installations

Let’s take a step back and understand how Chrome is installed.

When you download and run the MSI provided by Google, the first thing that is installed is the Google Update Helper. When this installs it still registers with Windows Installer that Chrome has been installed. The issue with this is that it’s registered with the package version, not the application version (e.g. Chrome 41 will appear in add/remove programs and registry as 66.56.118 when it should really be 41.0.2272.118).

Google Update Helper then handles the installation of Chrome itself as outside of the previous MSI you just ran, and in the process should update everything to reflect the correct version number. Sadly this doesn’t always happen

Removing the middle-man

The majority of the issues are around Google Update Helper, so let’s try installing Chrome without it.

If you are looking for a switch for the MSI, you are not going to find one. Remember that the MSI installed the Update Helper, not Chrome.

What we can see though is that in C:\Program Files (x86)\Google\Update\Download, there are several folders, one of which contains an EXE.

Chrome download folder

Installing Chrome

Now that we have the installer, we need to get it into SCCM.As with most applications, you should rename it to something that is readable and move it to the correct source location

There are a number of install switches available for the Chrome executable, but the only one we care about at this point is “–system-level”. If we don’t put this switch here, Chrome will install using the user context and go into AppData.

Google Chrome Install String

Uninstalling Chrome

Uninstalling Chrome follows the same process as installing Chrome. It is important to note that you will need to use the original installation media to perform the uninstall.

The correct switches to use are “–uninstall –system-level –force-uninstall”.

Google Chrome uninstall String

Detecting Chrome

Unfortunately, because we have bypassed the Chrome MSI, there is no registration with Windows Installer, and no Registry keys that you can use for detection.

For this reason, we will have to default back to folder detection.

If you are catering for both 32-Bit and 64-Bit, you will notice that the installation is placed into the exact same location, and contains almost identical files.

For my detection, I used the folder “%ProgramFiles(x86)%\Google\Chrome\Application\62.0.3202.94\WidevineCdm\_platform_specific\win_x64”.

As you can see in the string, it caters for both the version number and also for architectureGoogle Chrome Detection Method

PowerShell 4.0 – Get-MSIProperties

Anyone who uses SCCM for application deployments knows the frustration of having to find MSI product codes.

There are a few ways to make this process easier. My preferred method has always been to use the Detection Method tab to extract the product code that I can then copy/paste into the other tabs but when you are creating large amounts of applications this can be a very slow process.

So using PowerShell we can get a list of MSI properties without having to install the MSI or use additional steps in SCCM.

The function in this script can also be used in bigger scripts such as scripts to automate application installation (Something I will post about shortly)

I would like to thank Nickolaj Andersen (http://www.scconfigmgr.com/) for some of the code snippets


###############################################################################
####                                                                       ####
####                                                                       ####
####                          Get-MSIProperties                            ####
####                            Version 1.0.0                              ####
####                       Created by: Liam Matthews                       ####
####                                                                       ####
####                                                                       ####
###############################################################################

################################Release notes##################################
####                                                                       ####
################################### 1.0.0 #####################################
#### - Initial release                                                     ####
####                                                                       ####
###############################################################################

<#
.SYNOPSIS
    Get-MSIProperties retrieves all properties for the specified MSI(s)

.DESCRIPTION
    Get-MSIProperties retrieves all properties for the specified MSI(s)

.PARAMETER File
    Single msi file. Can be full path ot file name if in current location

    Example: C:\Temp\My.msi
    Example: my.msi

.PARAMETER Folder
    Specify a folder name. The all msi files is any subfolders will be included

    Example: C:\Temp

.PARAMETER Output
    Outputs the data to a html file in the script launch folder

.PARAMETER Log
    Outputs script running data to log file

.EXAMPLE
    Get-MSIProperties -File My.msi

    FileName                  Manufacturer ProductName               ProductVersion ProductLanguage ProductCode
    --------                  ------------ -----------               -------------- --------------- -----------
    H:\Software\7z920-x64.msi  Igor Pavlov  7-Zip 9.20 (x64 edition)  9.20.00.0      1033            {23170F69-40C1-2702-0920-000001000000}

    The above command gathers msi properties on the specified file 

.EXAMPLE
    Get-MSIProperties -Folder C:\Temp -Output

    FileName                                   Path                                               Manufacturer  ProductName       ProductVersion ProductLanguage ProductCode
    --------                                   ----                                               ------------  -----------       -------------- --------------- -----------
    GoogleChromeStandaloneEnterprise.msi       H:\Software\Google\Chrome                           Google, Inc.  Google Chrome     65.240.16509   1033            {80E666DA-3CC1-3476-9968-029D9F1FEB8F}
    Google Chrome Enterprise 32.0.1700.107.msi H:\Software\Google\Chrome\Enterprise\32.0.1700.107  Google, Inc.  Google Chrome     65.169.107     1033            {56B708CC-28A0-3CFC-A83B-BE70E5C4EA18}
    Google Chrome Enterprise 33.0.1750.154.msi H:\Software\Google\Chrome\Enterprise\33.0.1750.154  Google, Inc.  Google Chrome     65.181.32922   1033            {FBD50733-2ABE-3D23-88B4-7B0C0A0ADDA0}
    Google Earth.msi                           H:\Software\Google\Earth\Free\7.1.2.2041            Google        Google Earth      7.1.2.2041     1033            {4D2A6330-2F8B-11E3-9C40-B8AC6F97B88E}
    Google Earth Pro.msi                       H:\Software\Google\Earth\Pro\7.1.2.2041             Google        Google Earth Pro  7.1.2.2041     1033            {44FC61F0-2F8A-11E3-8CAE-B8AC6F97B88E}         

    The above command checks if the specified HotFixID is installed on the list of computers contained in a text file and outputs the results to a html file in the script launch folder

.NOTES
    Version 1.0.0
    Created by Liam Matthews
    Credit to Nickolaj Andersen for code snippets (http://www.scconfigmgr.com/)
#>

[CmdletBinding(DefaultParameterSetName="File")]
Param
(
    # Argument: File
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="File",Position=0)]
    [String]$File,

    # Argument: Folder
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="Folder",Position=0)]
    [String]$Folder = ".\",

    # Argument: Output
    [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
    [Switch]$Output

)

#Declaring initial variables
$ScriptInfo = Get-PSCallStack
$StartTime = Get-Date -Format "HH:mm:ss.fff"
$StartDate = Get-Date -Format "MM-dd-yyyy"
$StartDateTime = Get-Date -Format "HHmmss"
$TZbias = (Get-WmiObject -Query "Select Bias from Win32_TimeZone").bias

#Function for fetching MSI properties
Function Get-MSIProperty ([string]$Path,[string]$Property)
{
    $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer
    $comObjWI = New-Object -ComObject WindowsInstaller.Installer
    $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase","InvokeMethod",$Null,$comObjWI,@("$Path",0))
    $Query = "SELECT Value FROM Property WHERE Property = '$Property'"
    $View = $MSIDatabase.GetType().InvokeMember("OpenView","InvokeMethod",$null,$MSIDatabase,($Query))
    $View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
    $Record = $View.GetType().InvokeMember("Fetch","InvokeMethod",$null,$View,$null)
    $Value = $Record.GetType().InvokeMember("StringData","GetProperty",$null,$Record,1)

    Return $Value
}

#Clear output table
$Results = @()

#Action if file specified
IF ($File)
{
    $Manufacturer = Get-MSIProperty -Path $File -Property "Manufacturer"
    $ProductName = Get-MSIProperty -Path $File -Property "ProductName"
    $ProductVersion = Get-MSIProperty -Path $File -Property "ProductVersion"
    $ProductLanguage = Get-MSIProperty -Path $File -Property "ProductLanguage"
    $ProductCode = Get-MSIProperty -Path $File -Property "ProductCode"

    #Build results table
    $Results += New-Object PSObject -Property ([ordered]@{
        "FileName" = "$File"
        "Manufacturer" = "$Manufacturer"
        "ProductName" = "$ProductName"
        "ProductVersion" = "$ProductVersion"
        "ProductLanguage" = "$ProductLanguage"
        "ProductCode" = "$ProductCode"
    })
}
#Action if folder specified
ELSE
{
    #Searching for MSI files in folder
    $AllMSIFiles = Get-ChildItem $Folder -Recurse -Filter *.msi

    #Run action for each MSI found
    foreach ($MSIFullName in $AllMSIFiles)
    {
        $Manufacturer = Get-MSIProperty -Path $MSIFullName.FullName -Property "Manufacturer"
        $ProductName = Get-MSIProperty -Path $MSIFullName.FullName -Property "ProductName"
        $ProductVersion = Get-MSIProperty -Path $MSIFullName.FullName -Property "ProductVersion"
        $ProductLanguage = Get-MSIProperty -Path $MSIFullName.FullName -Property "ProductLanguage"
        $ProductCode = Get-MSIProperty -Path $MSIFullName.FullName -Property "ProductCode"

        #Build results table
        $Results += New-Object PSObject -Property ([ordered]@{
            "FileName" = $MSIFullName.Name
            "Path" = $MSIFullName.Directory
            "Manufacturer" = "$Manufacturer"
           "ProductName" = "$ProductName"
            "ProductVersion" = "$ProductVersion"
            "ProductLanguage" = "$ProductLanguage"
            "ProductCode" = "$ProductCode"
        })

    }
}

#Output to file or screen
IF ($Output)
{
        #Output to file
        $Results | ConvertTo-Html | Out-File MSIProperties-$StartDateTime.html
}
ELSE
{
    #Output to screen
    $Results | Format-Table
}

MS16-072 – Don’t install this update

Yesterday Microsoft released a new security patch: KB3159398 which appears to be wreaking havoc for Systems Administrators everywhere

https://technet.microsoft.com/en-us/library/security/ms16-072.aspx?f=255&MSPPError=-2147217396

The update is supposed to fix a vulnerability in Group Policy that allows standard users to access elevated privileges but instead seems to be be preventing any group policies not user filtered by “Authenticated Users” from applying at all

For more information on this keep and eye on Reddit or Microsoft TechNet

Hopefully Microsoft can fix this issue quickly and release a new update

For anyone who uses SCCM and wants an uninstall application, one can be downloaded from here:

SCCM Export- Microsoft HotFix KB3159389

Windows PowerShell 4.0 – Get-HotFixIDStatus

Recently a client had a need for a way to check if a list of computers had a certain Microsoft HotFix without having to manually logon to each one and check. PowerShell to the rescue

For this script I have implemented several new things I have learned since my last post and have refined other processes


###############################################################################
####                                                                       ####
####                                                                       ####
####                          Get-HotFixIDStatus                           ####
####                            Version 3.1.2                              ####
####                       Created by: Liam Matthews                       ####
####                                                                       ####
####                                                                       ####
###############################################################################

################################Release notes##################################
####                                                                       ####
################################### 1.0.0 #####################################
#### - Initial release                                                     ####
####                                                                       ####
################################### 2.0.0 #####################################
#### - Added help context for PowerShell                                   ####
#### - Added input parameters                                              ####
#### - Modified to allow for single computer or list from txt file         ####
#### - Modified output function to be optional                             ####
#### - Created custom WMI function with timeout to fix hanging             ####
#### - Added error handling and output                                     ####
####                                                                       ####
################################### 2.1.0 #####################################
#### - Moved error handling to function                                    ####
#### - Fixed all updates showing as false                                  ####
#### - Additional queries no longer run if first one failed                ####
####                                                                       ####
################################### 2.2.0 #####################################
#### - Made global error variable to fix errors not outputting             ####
#### - Added shortened error messages                                      ####
#### - Added function to test connection to device                         ####
#### - Increased WMI query timeout to 60 seconds                           ####
####                                                                       ####
################################### 2.2.1 #####################################
#### - Added additional error messages for detection                       ####
####                                                                       ####
################################### 3.0.0 #####################################
#### - Added Support for logging                                           ####
#### - Added error handling for file import                                ####
#### - Change screen output to only occur if Output argument was not used  ####
####                                                                       ####
################################### 3.1.0 #####################################
#### - Added debug text                                                    ####
####                                                                       ####
################################### 3.1.1 #####################################
#### - Changed formatting                                                  ####
#### - fixed a bug causing multiple logs being written for the same error  ####
################################### 3.1.1 #####################################
#### - Removed positional requirement for output and log arguments         ####
####                                                                       ####
###############################################################################

<#
.SYNOPSIS
    Get-HotFixIDStatus Status retrieves all collections where the specified device has a membership

.DESCRIPTION
    Get-HotFixIDStatus Status retrieves all collections where the specified device has a membership

.PARAMETER HotFixID
    The HotFix humber as provided by Microsoft

    Example: KB1234567
    Example: 1234567

.PARAMETER Computer
    The name of the computer to fetch the information from

    Example: Client01

.PARAMETER ComputerList
    TXT file to fetch computer names from if more than one

    Example: List.txt

.PARAMETER Output
    Outputs the data to a html file in the script launch folder

.PARAMETER Log
    Outputs script running data to log file

.EXAMPLE
    Get-HotFixIDStatus -HotFixID KB1234567 -ComputerName Client01

    ComputerName                                           KB1234567 Installed                                    OperatingSystem
    ----                                                   -------------------                                    ---------------
    Client01                                               True                                                   Microsoft Windows Server 2003 R2 Standard Edition           

    The above command checks if the specified HotFixID is installed on the computer 

.EXAMPLE
    Get-HotFixIDStatus -HotFixID KB1234567 -ComputerList Server.txt -Output

    ComputerName                                           KB1234567 Installed                                    OperatingSystem
    ----                                                   -------------------                                    ---------------
    Client01                                               False                                                  Microsoft Windows Server 2003 R2 Standard Edition
    Client02                                               True                                                   Microsoft Windows Server 2003 R2 Enterprise Edition
    Client03                                               True                                                   Microsoft Windows Server 2003 R2 Standard Edition
    Client04                                               False                                                  Microsoft Windows Server 2003 R2 Standard Edition
    Client05                                               False                                                  Microsoft Windows Server 2003 R2 Enterprise Edition
    Client06                                               False                                                  Microsoft Windows Server 2003 R2 Standard Edition
    Client07                                               True                                                   Microsoft Windows Server 2003 R2 Standard Edition
    Client08                                               False                                                  Microsoft Windows Server 2003 R2 Standard Edition           

    The above command checks if the specified HotFixID is installed on the list of computers contained in a text file and outputs the results to a html file in the script launch folder

.NOTES
    Version 3.1.2
    Created by Liam Matthews
#>

#
[CmdletBinding(DefaultParameterSetName="ComputerName")]
Param
(
    # Argument: HotFixID
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
    [String]$HotFixID,

    # Argument: ComputerList
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="ComputerList",Position=1)]
    [String]$ComputerList,

    # Argument: ComputerName
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="ComputerName",Position=1)]
    [String]$ComputerName = "localhost",

    # Argument: Output
    [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
    [Switch]$Output,

    # Argument: Log
    [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
    [Switch]$Log
)

#Declaring initial variables
$ScriptInfo = Get-PSCallStack
$StartTime = Get-Date -Format "HH:mm:ss.fff"
$StartDate = Get-Date -Format "MM-dd-yyyy"
$StartDateTime = Get-Date -Format "HHmmss"
$TZbias = (Get-WmiObject -Query "Select Bias from Win32_TimeZone").bias

#Formatting HotFixID variable
$HotFixID = $HotFixID.Replace("KB","")

#Funtion for outputting data to log file
Function Output-Log([string]$LogText,[int]$Type = "1",[string]$Component = $ScriptInfo.Command,[string]$Context = "KB$HotFixID",[string]$OutputFile = "KB$HotFixID.log")
{
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($Component)`">"

    #Write to log
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$OutputFile"
}

#Output current status to log
IF ($Log)
{
    Output-log -LogText "+++Starting new thread+++"
}
IF ($Log)
{
    Output-log -LogText "Script loaded from: $($ScriptInfo.ScriptName)"
}
IF ($Log)
{
    Output-log -LogText "Script loaded with arguments: $($ScriptInfo.Arguments)"
}
IF ($Log)
{
    Output-log -LogText "Script loaded by: $env:USERNAME" -Type "1"
}
IF ($Log)
{
    Output-log -LogText "Preloading functions" -Type "1"
}

#Function for
Function Get-WMICustom([string]$ComputerName,[string]$Namespace = "root\cimv2",[string]$Class,[string]$Filter = "",[int]$Timeout = 60)
{
    #Clear error queue
    $Error.Clear()
    #Set default error action
    $ErrorActionPreference = "Stop"

    #Output current status to log
    IF ($Log)
    {
        Output-log -LogText "Running function: Get-WMICustom with arguments: -ComputerName $ComputerName -Namespace $Namespace -class $Class -Filder $Filter -Timeout $Timeout"
    }

    #Attempting scriptblock
    Try
    {
        #Output current status to log
        IF ($Log)
        {
            Output-log -LogText "Building query block"
        }

        #Building query block
        $ConnectionOptions = New-Object System.Management.ConnectionOptions
        $EnumerationOptions = New-Object System.Management.EnumerationOptions
        $TimeoutSeconds = New-Timespan -seconds $Timeout
        $EnumerationOptions.set_timeout($timeoutseconds)
        $AssembledPath = "\\" + $ComputerName + "\" + $Namespace
        $Scope = New-Object System.Management.ManagementScope $AssembledPath, $ConnectionOptions
        $Scope.Connect()
        $QueryString = "SELECT * FROM " + $Class

        IF($Filter.Length -gt 0)
        {
            $QueryString += " WHERE " + $Filter
        }

        $Query = New-Object System.Management.ObjectQuery $QueryString
        $Searcher = New-Object System.Management.ManagementObjectSearcher
        $Searcher.set_options($EnumerationOptions)
        $Searcher.Query = $QueryString
        $Searcher.Scope = $Scope

        #Output current status to log
        IF ($Log)
        {
            Output-log -LogText "Running query"
        }

        #Run query
        Trap { $_ } $Result = $Searcher.get()

        #Return results
        Return $Result
    }
    #Action to take on error
    Catch
    {
        #Action if access denied error
        IF ($_.Exception.Message -match "Access is denied")
        {
            $Global:ErrorMessage = "Access denied"
            IF ($Log)
            {
                Output-log -LogText "Failed to run WMI query: Access Denied" -Type 2
            }
        }

        #Action if time out error
        IF ($_.Exception.Message -match "Timed Out")
        {
            $Global:ErrorMessage = "Timed out while connecting to target"
            IF ($Log)
            {
                Output-log -LogText "Failed to run WMI query: Timed out while connecting to target" -Type 2
            }
        }

        #Action if insufficient memory error
        IF ($_.Exception.Message -match "Insufficient memory to continue the execution")
        {
            $Global:ErrorMessage = "Insufficient memory on target"
            IF ($Log)
            {
                Output-log -LogText "Failed to run WMI query: Insufficient memory on target" -Type 2
            }
        }

        #Action if RPC server busy error
        IF ($_.Exception.Message -match "The RPC server is too busy to complete this operation")
        {
            $Global:ErrorMessage = "RPC server busy"
            IF ($Log)
            {
                Output-log -LogText "Failed to run WMI query: RPC server busy" -Type 2
            }
        }

        #Action if error is unknown
        IF
        (
            $_.Exception.Message -notmatch "Access is denied" -and,
            $_.Exception.Message -notmatch "Timed Out" -and,
            $_.Exception.Message -notmatch "Insufficient memory to continue the execution" -and,
            $_.Exception.Message -notmatch "The RPC server is too busy to complete this operation"
        )
        {
            $Global:ErrorMessage = $_.Exception.Message
        }
    }
}

#Output current status to log
IF ($Log)
{
    Output-log -LogText "Get-WMICustom function loaded"
}

Function Get-DeviceOnline([string]$ComputerName,[int]$Count = 1)
{
    #Clear error queue
    $Error.Clear()
    #Set default error action
    $ErrorActionPreference = "Stop"

    #Attempting scriptblock
    Try
    {

        #Pinging target
        $Ping = Test-Connection -ComputerName $ComputerName -Count $Count

        #Returning results
        Return $Ping

    }
    #Action to take on error
    Catch
    {
        #Setting error output
        $Global:ErrorMessage = "Device offline"
    }
}

#Output current status to log
IF ($Log)
{
    Output-log -LogText "Get-DeviceOnline function loaded"
}

#Clearing results variable from any previous use
$Results = @()

#Action if ComputerList argument defined
IF ($ComputerList)
{

    #Attempting scriptblock
    Try
    {
        #Output current status to log
        IF ($Log)
        {
            Output-log -LogText "Loading computers from defined list: $ComputerList"
        }

        #Reading file
        $ComputerNames = Get-Content $ComputerList -ErrorAction Stop
    }

    #Action of error
    Catch
    {
        #Output current status to log
        IF ($Log)
        {
            Output-log -LogText "Failed to load file" -Type 3
        }
        IF ($Log)
        {
            Output-log -LogText $_.Exception.Message -Type 3
        }
        IF ($Log)
        {
            Output-log -LogText "Script Terminating" -Type 3
        }
        Exit
    }

    #Loop to run for each computer in list
    ForEach ($ComputerName in $ComputerNames)
    {
        #Displaying progress bar
        $PercentComplete = ($Results.Count)/($ComputerNames.Count)*100
        $PercentComplete = [math]::Round($PercentComplete)
        Write-Progress -Activity "Gathering information" -Status "$PercentComplete%: $ComputerName" -PercentComplete $PercentComplete

        #Clearing variables
        $DeviceName = ""
        $HotFixIDExists = ""
        $FullOSName = ""
        $ShortOSName = ""
        $Global:ErrorMessage = ""

        #Output current status to log
        IF ($Log)
        {
            Output-log -LogText "Testing if $ComputerName is online"
        }

        $DeviceOnline = Get-DeviceOnline -ComputerName $ComputerName

        #Output current status to log
        IF ($Log -and $DeviceOnline)
        {
            Output-log -LogText "$ComputerName is responding to ping"}ELSE{Output-log -LogText "$ComputerName is not responding to ping. Skipping" -Type "2" | Continue
        }

        #Output current status to log
        IF ($Global:ErrorMessage)
        {

        }
        ELSE
        {
            IF ($Log)
            {
                Output-log -LogText "Connecting to WMI and fetching computer name"
            }
        }

        #Connecting to WMI and fetching computer name
        IF ($Global:ErrorMessage)
        {

        }
        ELSE
        {
            $DeviceName = (Get-WMICustom -ComputerName $ComputerName -Class "Win32_ComputerSystem").Name
        }

        #Output current status to log
        IF ($Global:ErrorMessage)
        {
            IF ($Log)
            {
                Output-log -LogText "Failed to connect to WMI: $Global:ErrorMessage" -Type "3"
                $WMIErrorOutput = $true
            }
        }
        ELSE
        {
            IF ($Log)
            {
                Output-log -LogText "Successfully fetched $DeviceName from WMI"
            }
        }
        IF ($Global:ErrorMessage)
        {

        }
        ELSE
        {
            IF ($Log)
            {
                Output-log -LogText "Connecting to WMI and fetching Operating System"
            }
        }

        #Connecting to WMI and fetching Operating System
        IF ($Global:ErrorMessage)
        {

        }
        ELSE
        {
            $FullOSName = Get-WMICustom -ComputerName $ComputerName -Class "Win32_OperatingSystem"
        }
        IF ($Global:ErrorMessage)
        {
            $ShortOSName = "N/A"
        }
        ELSE
        {
            $ShortOSName = ($FullOSName.Name.Split("|"))[0]
        }

        #Output current status to log
        IF ($Global:ErrorMessage)
        {
            IF ($WMIErrorOutput)
            {

            }
            ELSE
            {
                IF ($Log)
                {
                    Output-log -LogText "Failed to connect to WMI: $Global:ErrorMessage" -Type "3"
                    $WMIErrorOutput = $true
                }
            }
        }
        ELSE
        {
            IF ($Log)
            {
                Output-log -LogText "Successfully fetched $ShortOSName from WMI"
            }
        }
        IF ($Global:ErrorMessage)
        {

        }
        ELSE
        {
            IF ($Log)
            {
                Output-log -LogText "Connecting to WMI and searching for KB$HotFixID"
            }
        }

        #Connecting to WMI and searching for hotfix
        IF ($Global:ErrorMessage)
        {

        }
        ELSE
        {
            $HotFixIDSearch = Get-WMICustom -ComputerName $ComputerName -Class "Win32_QuickFixEngineering" -Filter "HotFixID = 'KB$HotFixID'"
        }

        #Output current status to log
        IF ($Global:ErrorMessage)
        {
            IF ($WMIErrorOutput)
            {

            }
            ELSE
            {
                IF ($Log)
                {
                    Output-log -LogText "Failed to connect to WMI: $Global:ErrorMessage" -Type "3"
                    $WMIErrorOutput = $true
                }
            }
        }
        ELSE
        {
            IF ($Log -and $HotFixIDSearch.HotFixID -eq "KB$HotFixID")
            {
                Output-log -LogText "Successfully connected to WMI and located KB$KBHotFixID"
            }
            ELSE
            {
                Output-log -LogText "Successfully connected to WMI but was unable to locate KB$KBHotFixID"
            }
        }
        IF ($Global:ErrorMessage)
        {

        }
        ELSE
        {
            IF ($Log)
            {
                Output-log -LogText "Finished connecting to WMI with no errors"
            }
        }

        #Defining variables on error
        IF ($Global:ErrorMessage)
        {
            $DeviceName = $ComputerName + ": " + $ErrorMessage
        }
        IF ($HotFixIDSearch.HotFixID -eq "KB$HotFixID")
        {
            $HotFixIDExists = "True"
        }
        ELSE
        {
            $HotFixIDExists = "False"
        }
        IF ($Global:ErrorMessage)
        {
            $HotFixIDExists = "N/A"
        }

        #Output current status to log
        IF ($Log)
        {
            Output-log -LogText "Returning results"
        }

        #Building table
        $Results += New-Object PSObject -Property @{
            "ComputerName" = $DeviceName
            "OperatingSystem" = $ShortOSName
            "KB$HotFixID Installed" = $HotFixIDExists
        }
    }
}
ELSE
{
    #Output current status to log
    IF ($Log)
    {
        Output-log -LogText "Testing if $ComputerName is online"
    }

    $DeviceOnline = Get-DeviceOnline -ComputerName $ComputerName

    #Output current status to log
    IF ($Log -and $DeviceOnline)
    {
        Output-log -LogText "$ComputerName is responding to ping"}ELSE{Output-log -LogText "$ComputerName is not responding to ping. Skipping" -Type "2" | Continue
    }

    #Output current status to log
    IF ($Global:ErrorMessage)
    {

    }
    ELSE
    {
        IF ($Log)
        {
            Output-log -LogText "Connecting to WMI and fetching computer name"
        }
    }

    #Connecting to WMI and fetching computer name
    IF ($Global:ErrorMessage)
    {

    }
    ELSE
    {
        $DeviceName = (Get-WMICustom -ComputerName $ComputerName -Class "Win32_ComputerSystem").Name
    }

    #Output current status to log
    IF ($Global:ErrorMessage)
    {
        IF ($Log)
        {
            Output-log -LogText "Failed to connect to WMI: $Global:ErrorMessage" -Type "3"
            $WMIErrorOutput = $true
        }
    }
    ELSE
    {
        IF ($Log)
        {
            Output-log -LogText "Successfully fetched $DeviceName from WMI"
        }
    }
    IF ($Global:ErrorMessage)
    {

    }
    ELSE
    {
        IF ($Log)
        {
            Output-log -LogText "Connecting to WMI and fetching Operating System"
        }
    }

    #Connecting to WMI and fetching Operating System
    IF ($Global:ErrorMessage)
    {

    }
    ELSE
    {
        $FullOSName = Get-WMICustom -ComputerName $ComputerName -Class "Win32_OperatingSystem"
    }
    IF ($Global:ErrorMessage)
    {
        $ShortOSName = "N/A"
    }
    ELSE
    {
        $ShortOSName = ($FullOSName.Name.Split("|"))[0]
    }

    #Output current status to log
    IF ($Global:ErrorMessage)
    {
        IF ($WMIErrorOutput)
        {

        }
        ELSE
        {
            IF ($Log)
            {
                Output-log -LogText "Failed to connect to WMI: $Global:ErrorMessage" -Type "3"
                $WMIErrorOutput = $true
            }
        }
    }
    ELSE
    {
        IF ($Log)
        {
            Output-log -LogText "Successfully fetched $ShortOSName from WMI"
        }
    }
    IF ($Global:ErrorMessage)
    {

    }
    ELSE
    {
        IF ($Log)
        {
            Output-log -LogText "Connecting to WMI and searching for KB$HotFixID"
        }
    }

    #Connecting to WMI and searching for hotfix
    IF ($Global:ErrorMessage)
    {

    }
    ELSE
    {
        $HotFixIDSearch = Get-WMICustom -ComputerName $ComputerName -Class "Win32_QuickFixEngineering" -Filter "HotFixID = 'KB$HotFixID'"
    }

    #Output current status to log
    IF ($Global:ErrorMessage)
    {
        IF ($WMIErrorOutput)
        {

        }
        ELSE
        {
            IF ($Log)
            {
                Output-log -LogText "Failed to connect to WMI: $Global:ErrorMessage" -Type "3"
                $WMIErrorOutput = $true
            }
        }
    }
    ELSE
    {
        IF ($Log -and $HotFixIDSearch.HotFixID -eq "KB$HotFixID")
        {
            Output-log -LogText "Successfully connected to WMI and located KB$KBHotFixID"
        }
        ELSE
        {
            Output-log -LogText "Successfully connected to WMI but was unable to locate KB$KBHotFixID"
        }
    }
    IF ($Global:ErrorMessage)
    {

    }
    ELSE
    {
        IF ($Log)
        {
            Output-log -LogText "Finished connecting to WMI with no errors"
        }
    }

    #Defining variables on error
    IF ($Global:ErrorMessage)
    {
        $DeviceName = $ComputerName + ": " + $ErrorMessage
    }
    IF ($HotFixIDSearch.HotFixID -eq "KB$HotFixID")
    {
        $HotFixIDExists = "True"
    }
    ELSE
    {
        $HotFixIDExists = "False"
    }
    IF ($Global:ErrorMessage)
    {
        $HotFixIDExists = "N/A"
    }

    #Output current status to log
    IF ($Log)
    {
        Output-log -LogText "Returning results"
    }

    #Building table
    $Results += New-Object PSObject -Property @{
        "ComputerName" = $DeviceName
        "OperatingSystem" = $ShortOSName
        "KB$HotFixID Installed" = $HotFixIDExists
    }
}

#Output current status to log
IF ($Log -and $Output)
{
    Output-log -LogText "Saving results to KB$HotFixID.html"
}

#Output to file or screen
IF ($Output)
{
    $Results | Sort-Object ComputerName,
    @{
        expression="ComputerName";Descending=$false
    } | ConvertTo-Html -Head $Header | Out-File KB$HotFixID.html
}
ELSE
{
    $Results
}

Windows PowerShell 4.0 – EMCOPY GUI script

It seems like I am doing heaps of PowerShell scripts lately, but once people find out that you have some experience with it and what it can do they will start lining up.

For this script I was asked to develop a GUI for EMCOPY which is sort of like robocopy but from EMC. There are many switches available and the admin who currently use it tell me that they need to be placed in certain orders and getting this wrong can have drastic consequences. It also needs to be handed to a level one team so it needs to be reasonably simple and foolproof.

This script introduced some new functions that I had not used previously, specifically live modifying of items within a form.


###############################################################################
####                                                                       ####
####                                                                       ####
####                            EMCopy Script                              ####
####                            Version 2.1.2                              ####
####                       Created by: Liam Matthews                       ####
####                                                                       ####
####                                                                       ####
###############################################################################

################################Release notes##################################
####                                                                       ####
################################### 1.0.0 #####################################
#### - Initial release                                                     ####
####                                                                       ####
################################### 1.0.1 #####################################
#### - Anchored the Preview bar to the bottom of the window                ####
#### - Disabled preview text box                                           ####
####                                                                       ####
################################### 2.0.0 #####################################
#### - Added simple screen                                                 ####
#### - Changed log location to be destination folder                       ####
#### - Added /nosec option                                                 ####
####                                                                       ####
################################### 2.0.1 #####################################
#### - Added space betweek /c and /log for preview text                    ####
#### - Added simple options to run command                                 ####
####                                                                       ####
################################### 2.0.2 #####################################
#### - Fixed log file name to contain -LOG on preview and run              ####
####                                                                       ####
################################### 2.0.3 #####################################
#### - Forced script to ignore all errors                                  ####
#### - Added path testing function for source and destination              ####
#### - Run button now enabled by both correct source and destination paths ####
####                                                                       ####
################################### 2.1.0 #####################################
#### - Added confirmation dialog box                                       ####
#### - Modified variables to be global                                     ####
#### - Fixed bug preventing final action from running                      ####
####                                                                       ####
################################### 2.1.1 #####################################
#### - Changed program launcher to Start-Process command                   ####
####                                                                       ####
################################### 2.1.2 #####################################
#### - Changed confirm box and run action to functions                     ####
#### - Changed more variables to be global variables                       ####
#### - Added font to confirm box to fix box not displaying                 ####
####                                                                       ####
###############################################################################

$ErrorActionPreference = 'silentlycontinue'

function EMCOPY
{
 [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
 [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

 $Action = ""

 #Get date and time
 $Date = Get-Date -Format "yyyy-mm-dd--HH.mm.ss"

 #Set form size
 $Form = New-Object System.Windows.Forms.Form
 $Form.width = 1000
 $Form.height = 850
 $Form.WindowState = "Maximized"
 $Form.Text = "EMCopy options"
 $Form.AutoScroll = $True

 #Set the font of the text to be used within the form
 $Font = New-Object System.Drawing.Font("Times New Roman",12)
 $Form.Font = $Font

 #Create Source CheckBox
 $Global:CheckBox1 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox1.Location = new-object System.Drawing.Size(20,0)
 $Global:CheckBox1.Size = new-object System.Drawing.Size(250,50)
 $Global:CheckBox1.Text = "Source"
 $Global:CheckBox1.Checked = $False
 $Global:CheckBox1.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox1)

 #Create Source TextBox
 $Global:TextBox1 = New-Object System.Windows.Forms.TextBox
 $Global:TextBox1.Location = New-Object System.Drawing.Size(300,10)
 $Global:TextBox1.Size = New-Object System.Drawing.Size(670,50)
 $Global:TextBox1.TabIndex = 1
 $Global:TextBox1.Enabled = $False
 $Global:TextBox1.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $Form.Controls.Add($Global:TextBox1)

 #Create Destination CheckBox
 $Global:CheckBox2 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox2.Location = new-object System.Drawing.Size(20,40)
 $Global:CheckBox2.Size = new-object System.Drawing.Size(250,50)
 $Global:CheckBox2.Text = "Destination"
 $Global:CheckBox2.Checked = $False
 $Global:CheckBox2.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox2)

 #Create Destination TextBox
 $Global:TextBox2 = New-Object System.Windows.Forms.TextBox
 $Global:TextBox2.Location = New-Object System.Drawing.Size(300,50)
 $Global:TextBox2.Size = New-Object System.Drawing.Size(670,50)
 $Global:TextBox2.TabIndex = 1
 $Global:TextBox2.Enabled = $False
 $Global:TextBox2.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $Form.Controls.Add($Global:TextBox2)

 #Create /S CheckBox
 $Global:CheckBox3 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox3.Location = new-object System.Drawing.Size(20,80)
 $Global:CheckBox3.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox3.Text = "Copy all files in the subdirectories"
 $Global:CheckBox3.Checked = $False
 $Global:CheckBox3.Visible = $False
 $Global:CheckBox3.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox3)

 #Create /purge CheckBox
 $Global:CheckBox4 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox4.Location = new-object System.Drawing.Size(20,120)
 $Global:CheckBox4.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox4.Text = "Remove extra files and directories from the destination tree"
 $Global:CheckBox4.Checked = $False
 $Global:CheckBox4.Visible = $False
 $Global:CheckBox4.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox4)

 #Create /SDD CheckBox
 $Global:CheckBox5 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox5.Location = new-object System.Drawing.Size(20,160)
 $Global:CheckBox5.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox5.Text = "Force copy of 'Creation Date' to destination"
 $Global:CheckBox5.Checked = $False
 $Global:CheckBox5.Visible = $False
 $Global:CheckBox5.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox5)

 #Create /D CheckBox
 $Global:CheckBox6 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox6.Location = new-object System.Drawing.Size(20,200)
 $Global:CheckBox6.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox6.Text = "Copy only the source files when its last modification time is greater than the destination file’s modification time"
 $Global:CheckBox6.Checked = $False
 $Global:CheckBox6.Visible = $False
 $Global:CheckBox6.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox6)

 #Create /DE CheckBox
 $Global:CheckBox7 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox7.Location = new-object System.Drawing.Size(20,240)
 $Global:CheckBox7.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox7.Text = "Copy the source file when its last modification time is not equal to the destination file’s modification time or when files size are different"
 $Global:CheckBox7.Checked = $False
 $Global:CheckBox7.Visible = $False
 $Global:CheckBox7.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox7)

 #Create /O CheckBox
 $Global:CheckBox8 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox8.Location = new-object System.Drawing.Size(20,280)
 $Global:CheckBox8.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox8.Text = "Copy owner security information"
 $Global:CheckBox8.Checked = $False
 $Global:CheckBox8.Visible = $False
 $Global:CheckBox8.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox8)

 #Create /secfix CheckBox
 $Global:CheckBox9 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox9.Location = new-object System.Drawing.Size(20,320)
 $Global:CheckBox9.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox9.Text = "Fix the NT security properties on existing destination files or directories"
 $Global:CheckBox9.Checked = $False
 $Global:CheckBox9.Visible = $False
 $Global:CheckBox9.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox9)

 #Create /secforce CheckBox
 $Global:CheckBox10 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox10.Location = new-object System.Drawing.Size(20,360)
 $Global:CheckBox10.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox10.Text = "Overwrite NT security properties"
 $Global:CheckBox10.Checked = $False
 $Global:CheckBox10.Visible = $False
 $Global:CheckBox10.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox10)

 #Create /nosec CheckBox
 $Global:CheckBox11 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox11.Location = new-object System.Drawing.Size(20,400)
 $Global:CheckBox11.Size = new-object System.Drawing.Size(670,50)
 $Global:CheckBox11.Text = "Disable copy of NT security properties"
 $Global:CheckBox11.Checked = $False
 $Global:CheckBox11.Visible = $False
 $Global:CheckBox11.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox11)

 #Create /LG CheckBox
 $Global:CheckBox12 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox12.Location = new-object System.Drawing.Size(20,440)
 $Global:CheckBox12.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox12.Text = "Force copy local group SID"
 $Global:CheckBox12.Checked = $False
 $Global:CheckBox12.Visible = $False
 $Global:CheckBox12.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox12)

 #Create /TH CheckBox
 $Global:CheckBox13 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox13.Location = new-object System.Drawing.Size(20,480)
 $Global:CheckBox13.Size = new-object System.Drawing.Size(250,50)
 $Global:CheckBox13.Text = "Number of working threads"
 $Global:CheckBox13.Checked = $False
 $Global:CheckBox13.Visible = $False
 $Global:CheckBox13.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox13)

 #Create /TH TextBox
 $Global:TextBox3 = New-Object System.Windows.Forms.TextBox
 $Global:TextBox3.Location = New-Object System.Drawing.Size(300,490)
 $Global:TextBox3.Size = New-Object System.Drawing.Size(670,50)
 $Global:TextBox3.TabIndex = 1
 $Global:TextBox3.Enabled = $False
 $Global:TextBox3.Visible = $False
 $Global:TextBox3.Text = "64"
 $Global:TextBox3.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $Form.Controls.Add($Global:TextBox3)

 #Create /R: CheckBox
 $Global:CheckBox14 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox14.Location = new-object System.Drawing.Size(20,520)
 $Global:CheckBox14.Size = new-object System.Drawing.Size(250,50)
 $Global:CheckBox14.Text = "Number of retries"
 $Global:CheckBox14.Checked = $False
 $Global:CheckBox14.Visible = $False
 $Global:CheckBox14.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox14)

 #Create /R: TextBox
 $Global:TextBox4 = New-Object System.Windows.Forms.TextBox
 $Global:TextBox4.Location = New-Object System.Drawing.Size(300,530)
 $Global:TextBox4.Size = New-Object System.Drawing.Size(670,50)
 $Global:TextBox4.TabIndex = 1
 $Global:TextBox4.Enabled = $False
 $Global:TextBox4.Visible = $False
 $Global:TextBox4.Text = "100"
 $Global:TextBox4.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $Form.Controls.Add($Global:TextBox4)

 #Create /W: CheckBox
 $Global:CheckBox15 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox15.Location = new-object System.Drawing.Size(20,560)
 $Global:CheckBox15.Size = new-object System.Drawing.Size(250,50)
 $Global:CheckBox15.Text = "Wait between retries (Seconds)"
 $Global:CheckBox15.Checked = $False
 $Global:CheckBox15.Visible = $False
 $Global:CheckBox15.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox15)

 #Create /W: TextBox
 $Global:TextBox5 = New-Object System.Windows.Forms.TextBox
 $Global:TextBox5.Location = New-Object System.Drawing.Size(300,570)
 $Global:TextBox5.Size = New-Object System.Drawing.Size(670,50)
 $Global:TextBox5.TabIndex = 1
 $Global:TextBox5.Enabled = $False
 $Global:TextBox5.Visible = $False
 $Global:TextBox5.Text = "30"
 $Global:TextBox5.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $Form.Controls.Add($Global:TextBox5)

 #Create /C CheckBox
 $Global:CheckBox16 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox16.Location = new-object System.Drawing.Size(20,600)
 $Global:CheckBox16.Size = new-object System.Drawing.Size(950,50)
 $Global:CheckBox16.Text = "Continue after the number of retries"
 $Global:CheckBox16.Checked = $False
 $Global:CheckBox16.Visible = $False
 $Global:CheckBox16.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox16)

 #Create /LOG CheckBox
 $Global:CheckBox17 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox17.Location = new-object System.Drawing.Size(20,640)
 $Global:CheckBox17.Size = new-object System.Drawing.Size(250,50)
 $Global:CheckBox17.Text = "Log file"
 $Global:CheckBox17.Checked = $False
 $Global:CheckBox17.Visible = $False
 $Global:CheckBox17.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox17)

 #Create /LOG TextBox
 $Global:TextBox6 = New-Object System.Windows.Forms.TextBox
 $Global:TextBox6.Location = New-Object System.Drawing.Size(300,650)
 $Global:TextBox6.Size = New-Object System.Drawing.Size(670,50)
 $Global:TextBox6.TabIndex = 1
 $Global:TextBox6.Enabled = $False
 $Global:TextBox6.Visible = $False
 $Global:TextBox6.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $Form.Controls.Add($Global:TextBox6)

 #Add a simple button
 $SimpleButton = new-object System.Windows.Forms.Button
 $SimpleButton.Location = new-object System.Drawing.Size(10,730)
 $SimpleButton.Size = new-object System.Drawing.Size(80,30)
 $SimpleButton.Text = "Simple"
 $SimpleButton.Add_Click({
 $Global:CheckBox3.Visible = $False
 $Global:CheckBox4.Visible = $False
 $Global:CheckBox5.Visible = $False
 $Global:CheckBox6.Visible = $False
 $Global:CheckBox7.Visible = $False
 $Global:CheckBox8.Visible = $False
 $Global:CheckBox9.Visible = $False
 $Global:CheckBox10.Visible = $False
 $Global:CheckBox11.Visible = $False
 $Global:CheckBox12.Visible = $False
 $Global:CheckBox13.Visible = $False
 $Global:CheckBox14.Visible = $False
 $Global:CheckBox15.Visible = $False
 $Global:CheckBox16.Visible = $False
 $Global:CheckBox17.Visible = $False
 $Global:CheckBox18.Visible = $True
 $Global:CheckBox19.Visible = $True
 $Global:CheckBox20.Visible = $True
 $Global:CheckBox21.Visible = $True
 $Global:CheckBox22.Visible = $True
 $Global:TextBox3.Visible = $False
 $Global:TextBox4.Visible = $False
 $Global:TextBox5.Visible = $False
 $Global:TextBox6.Visible = $False
 $Global:CheckBox3.Checked = $False
 $Global:CheckBox4.Checked = $False
 $Global:CheckBox5.Checked = $False
 $Global:CheckBox6.Checked = $False
 $Global:CheckBox7.Checked = $False
 $Global:CheckBox8.Checked = $False
 $Global:CheckBox9.Checked = $False
 $Global:CheckBox10.Checked = $False
 $Global:CheckBox11.Checked = $False
 $Global:CheckBox12.Checked = $False
 $Global:CheckBox13.Checked = $False
 $Global:CheckBox14.Checked = $False
 $Global:CheckBox15.Checked = $False
 $Global:CheckBox16.Checked = $False
 $Global:CheckBox17.Checked = $False
 $SimpleButton.Enabled = $False
 $AdvancedButton.Enabled = $True
 })
 $SimpleButton.Enabled = $False
 $SimpleButton.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Left
 $form.Controls.Add($SimpleButton)

 #Add an advanced button
 $AdvancedButton = new-object System.Windows.Forms.Button
 $AdvancedButton.Location = new-object System.Drawing.Size(100,730)
 $AdvancedButton.Size = new-object System.Drawing.Size(100,30)
 $AdvancedButton.Text = "Advanced"
 $AdvancedButton.Add_Click({
 $Global:CheckBox3.Visible = $True
 $Global:CheckBox4.Visible = $True
 $Global:CheckBox5.Visible = $True
 $Global:CheckBox6.Visible = $True
 $Global:CheckBox7.Visible = $True
 $Global:CheckBox8.Visible = $True
 $Global:CheckBox9.Visible = $True
 $Global:CheckBox10.Visible = $True
 $Global:CheckBox11.Visible = $True
 $Global:CheckBox12.Visible = $True
 $Global:CheckBox13.Visible = $True
 $Global:CheckBox14.Visible = $True
 $Global:CheckBox15.Visible = $True
 $Global:CheckBox16.Visible = $True
 $Global:CheckBox17.Visible = $True
 $Global:CheckBox18.Visible = $False
 $Global:CheckBox19.Visible = $False
 $Global:CheckBox20.Visible = $False
 $Global:CheckBox21.Visible = $False
 $Global:CheckBox22.Visible = $False
 $Global:TextBox3.Enabled = $False
 $Global:TextBox4.Enabled = $False
 $Global:TextBox5.Enabled = $False
 $Global:TextBox6.Enabled = $False
 $Global:TextBox3.Visible = $True
 $Global:TextBox4.Visible = $True
 $Global:TextBox5.Visible = $True
 $Global:TextBox6.Visible = $True
 $Global:CheckBox18.Checked = $False
 $Global:CheckBox19.Checked = $False
 $Global:CheckBox20.Checked = $False
 $Global:CheckBox21.Checked = $False
 $Global:CheckBox22.Checked = $False
 $SimpleButton.Enabled = $True
 $AdvancedButton.Enabled = $False
 })
 $AdvancedButton.Enabled = $True
 $AdvancedButton.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Left
 $form.Controls.Add($AdvancedButton)

 #Create Option 1 CheckBox
 $Global:CheckBox18 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox18.Location = new-object System.Drawing.Size(20,80)
 $Global:CheckBox18.Size = new-object System.Drawing.Size(670,50)
 $Global:CheckBox18.Text = "Source physical file server - Destination physical file server"
 $Global:CheckBox18.Checked = $False
 $Global:CheckBox18.Visible = $True
 $Global:CheckBox18.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox18)

 #Create Option 2 CheckBox
 $Global:CheckBox19 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox19.Location = new-object System.Drawing.Size(20,120)
 $Global:CheckBox19.Size = new-object System.Drawing.Size(670,50)
 $Global:CheckBox19.Text = "Source physical file server different domain - Destination file server"
 $Global:CheckBox19.Checked = $False
 $Global:CheckBox19.Visible = $True
 $Global:CheckBox19.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox19)

 #Create Option 3 CheckBox
 $Global:CheckBox20 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox20.Location = new-object System.Drawing.Size(20,160)
 $Global:CheckBox20.Size = new-object System.Drawing.Size(670,50)
 $Global:CheckBox20.Text = "Source physical file server same or different domain - Destination NAS"
 $Global:CheckBox20.Checked = $False
 $Global:CheckBox20.Visible = $True
 $Global:CheckBox20.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox20)

 #Create Option 4 CheckBox
 $Global:CheckBox21 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox21.Location = new-object System.Drawing.Size(20,200)
 $Global:CheckBox21.Size = new-object System.Drawing.Size(670,50)
 $Global:CheckBox21.Text = "Source NAS same domain - Destination NAS"
 $Global:CheckBox21.Checked = $False
 $Global:CheckBox21.Visible = $True
 $Global:CheckBox21.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox21)

 #Create Option 5 CheckBox
 $Global:CheckBox22 = new-object System.Windows.Forms.CheckBox
 $Global:CheckBox22.Location = new-object System.Drawing.Size(20,240)
 $Global:CheckBox22.Size = new-object System.Drawing.Size(670,50)
 $Global:CheckBox22.Text = "Source NAS different domain - Destination NAS"
 $Global:CheckBox22.Checked = $False
 $Global:CheckBox22.Visible = $True
 $Global:CheckBox22.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $Form.Controls.Add($Global:CheckBox22)

 #Create preview TextBox
 $Global:TextBox7 = New-Object System.Windows.Forms.TextBox
 $Global:TextBox7.Location = New-Object System.Drawing.Size(100,770)
 $Global:TextBox7.Size = New-Object System.Drawing.Size(700,50)
 $Global:TextBox7.TabIndex = 1
 $Global:TextBox7.Enabled = $False
 $Global:TextBox7.Anchor = [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $Form.Controls.Add($Global:TextBox7)

 #Add a preview button
 $PreviewButton = new-object System.Windows.Forms.Button
 $PreviewButton.Location = new-object System.Drawing.Size(10,770)
 $PreviewButton.Size = new-object System.Drawing.Size(80,30)
 $PreviewButton.Text = "Preview"
 $PreviewButton.Add_Click({
 $Global:Option1 = ""
 $Global:Option2 = ""
 $Global:Option3 = ""
 $Global:Option4 = ""
 $Global:Option5 = ""
 $Global:Option6 = ""
 $Global:Option7 = ""
 $Global:Option8 = ""
 $Global:Option9 = ""
 $Global:Option10 = ""
 $Global:Option11 = ""
 $Global:Option12 = ""
 $Global:Option13 = ""
 $Global:Option14 = ""
 $Global:Option15 = ""
 $Global:Option16 = ""
 $Global:Option17 = ""
 $Global:Option18 = ""
 $Global:Option19 = ""
 $Global:Option20 = ""

 If ($Global:CheckBox1.Checked){$Global:Source = $Global:TextBox1.Text}
 If ($Global:CheckBox2.Checked){$Global:Destination = $Global:TextBox2.Text}
 If ($Global:CheckBox3.Checked){$Global:Option1 = "/S"}
 If ($Global:CheckBox4.Checked){$Global:Option2 = "/purge"}
 If ($Global:CheckBox5.Checked){$Global:Option3 = "/SDD"}
 If ($Global:CheckBox6.Checked){$Global:Option4 = "/D"}
 If ($Global:CheckBox7.Checked){$Global:Option5 = "/DE"}
 If ($Global:CheckBox8.Checked){$Global:Option6 = "/O"}
 If ($Global:CheckBox9.Checked){$Global:Option7 = "/secfix"}
 If ($Global:CheckBox10.Checked){$Global:Option8 = "/secforce"}
 If ($Global:CheckBox11.Checked){$Global:Option9 = "/nosec"}
 If ($Global:CheckBox12.Checked){$Global:Option10 = "/LG"}
 If ($Global:CheckBox13.Checked){$Global:Option11 = "/TH " + $Global:TextBox3.Text}
 If ($Global:CheckBox14.Checked){$Global:Option12 = "/R:" + $Global:TextBox4.Text}
 If ($Global:CheckBox15.Checked){$Global:Option13 = "/W:" + $Global:TextBox5.Text}
 If ($Global:CheckBox16.Checked){$Global:Option14 = "/C"}
 If ($Global:CheckBox17.Checked){$Global:Option15 = "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox18.Checked){$Global:Option16 = "/s /nosec /purge /sdd /de /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox19.Checked){$Global:Option17 = "/s /nosec /purge /sdd /de /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox20.Checked){$Global:Option18 = "/s /purge /sdd /de /o /secfix /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox21.Checked){$Global:Option19 = "/s purge /sdd /de /o /secfix /lg /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox22.Checked){$Global:Option20 = "/s /nosec /purge /sdd /de /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}

 $PreviewText = "EMCOPY64.exe " + $Global:Source + " " + $Global:Destination + " " + $Global:Option1 + " " + $Global:Option2 + " " + $Global:Option3 + " " + $Global:Option4 + " " + $Global:Option5 + " " + $Global:Option6 + " " + $Global:Option7 + " " + $Global:Option8 + " " + $Global:Option9 + " " + $Global:Option10 + " " + $Global:Option11 + " " + $Global:Option12 + " " + $Global:Option13 + " " + $Global:Option14 + " " + $Global:Option15 + " " + $Global:Option16 + " " + $Global:Option17 + " " + $Global:Option18 + " " + $Global:Option19 + " " + $Global:Option20
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $PreviewText = $PreviewText.Replace(" "," ")
 $Global:TextBox7.Text = $PreviewText})
 $PreviewButton.Enabled = $True
 $PreviewButton.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Left
 $form.Controls.Add($PreviewButton)

 #Add a run button
 $RunButton = new-object System.Windows.Forms.Button
 $RunButton.Location = new-object System.Drawing.Size(880,770)
 $RunButton.Size = new-object System.Drawing.Size(80,30)
 $RunButton.Text = "Run"
 $RunButton.Add_Click({
 $global:Action = "Run"
 $Form.Close()
 })
 $RunButton.Enabled = $False
 $RunButton.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $form.Controls.Add($RunButton)

 #Add a cancel button
 $CancelButton = new-object System.Windows.Forms.Button
 $CancelButton.Location = new-object System.Drawing.Size(810,770)
 $CancelButton.Size = new-object System.Drawing.Size(60,30)
 $CancelButton.Text = "Cancel"
 $CancelButton.Add_Click({
 $global:Action = "Cancel"
 $Form.Close()})
 $CancelButton.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $form.Controls.Add($CancelButton)

 #CheckBox/TextBox state change options

 [void]$Global:TextBox1.Add_TextChanged({($RunButton.Enabled = $Global:TextBox1.Text) -and ($RunButton.Enabled = $Global:TextBox2.Text) -and ($RunButton.Enabled = Test-Path $Global:TextBox1.Text) -and ($RunButton.Enabled = Test-Path $Global:TextBox2.Text)})

 [void]$Global:TextBox2.Add_TextChanged({($RunButton.Enabled = $Global:TextBox1.Text) -and ($RunButton.Enabled = $Global:TextBox2.Text) -and ($RunButton.Enabled = Test-Path $Global:TextBox1.Text) -and ($RunButton.Enabled = Test-Path $Global:TextBox2.Text)})
 $Global:TextBox2.Add_TextChanged({$Global:TextBox6.Text = "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"})

 #$Global:CheckBox1.Add_CheckStateChanged({($RunButton.Enabled = $Global:CheckBox1.Checked) -and ($RunButton.Enabled = $Global:CheckBox2.Checked)})
 $Global:CheckBox1.Add_CheckStateChanged({$Global:TextBox1.Enabled = $Global:CheckBox1.Checked})
 $Global:CheckBox1.Add_CheckStateChanged({$Global:TextBox1.Text = ""})

 #$Global:CheckBox2.Add_CheckStateChanged({($RunButton.Enabled = $Global:CheckBox1.Checked) -and ($RunButton.Enabled = $Global:CheckBox2.Checked)})
 $Global:CheckBox2.Add_CheckStateChanged({$Global:TextBox2.Enabled = $Global:CheckBox2.Checked})
 $Global:CheckBox2.Add_CheckStateChanged({$Global:TextBox2.Text = ""})

 $Global:CheckBox6.Add_CheckStateChanged({$Global:CheckBox7.Enabled = $Global:CheckBox6.Checked -eq $False})

 $Global:CheckBox7.Add_CheckStateChanged({$Global:CheckBox6.Enabled = $Global:CheckBox7.Checked -eq $False})

 $Global:CheckBox9.Add_CheckStateChanged({$Global:CheckBox10.Enabled = $Global:CheckBox9.Checked -eq $False})
 $Global:CheckBox9.Add_CheckStateChanged({$Global:CheckBox11.Enabled = $Global:CheckBox9.Checked -eq $False})

 $Global:CheckBox10.Add_CheckStateChanged({$Global:CheckBox9.Enabled = $Global:CheckBox10.Checked -eq $False})
 $Global:CheckBox10.Add_CheckStateChanged({$Global:CheckBox11.Enabled = $Global:CheckBox10.Checked -eq $False})

 $Global:CheckBox11.Add_CheckStateChanged({$Global:CheckBox9.Enabled = $Global:CheckBox11.Checked -eq $False})
 $Global:CheckBox11.Add_CheckStateChanged({$Global:CheckBox10.Enabled = $Global:CheckBox11.Checked -eq $False})

 $Global:CheckBox13.Add_CheckStateChanged({$Global:TextBox3.Enabled = $Global:CheckBox13.Checked})
 $Global:CheckBox13.Add_CheckStateChanged({$Global:TextBox3.Text = "64"})

 $Global:CheckBox14.Add_CheckStateChanged({$Global:TextBox4.Enabled = $Global:CheckBox14.Checked})
 $Global:CheckBox14.Add_CheckStateChanged({$Global:TextBox4.Text = "100"})

 $Global:CheckBox15.Add_CheckStateChanged({$Global:TextBox5.Enabled = $Global:CheckBox15.Checked})
 $Global:CheckBox15.Add_CheckStateChanged({$Global:TextBox5.Text = "30"})

 $Global:CheckBox17.Add_CheckStateChanged({$Global:TextBox6.Enabled = $Global:CheckBox17.Checked})
 $Global:CheckBox17.Add_CheckStateChanged({$Global:TextBox6.Text = "c:\emcopy\copy_logs\" + (($Path = $Global:TextBox2.Text).Split("{\}")[-1]) + "-LOG.txt"})

 $Global:CheckBox18.Add_CheckStateChanged({$Global:CheckBox19.Enabled = $Global:CheckBox18.Checked -eq $False})
 $Global:CheckBox18.Add_CheckStateChanged({$Global:CheckBox20.Enabled = $Global:CheckBox18.Checked -eq $False})
 $Global:CheckBox18.Add_CheckStateChanged({$Global:CheckBox21.Enabled = $Global:CheckBox18.Checked -eq $False})
 $Global:CheckBox18.Add_CheckStateChanged({$Global:CheckBox22.Enabled = $Global:CheckBox18.Checked -eq $False})

 $Global:CheckBox19.Add_CheckStateChanged({$Global:CheckBox18.Enabled = $Global:CheckBox19.Checked -eq $False})
 $Global:CheckBox19.Add_CheckStateChanged({$Global:CheckBox20.Enabled = $Global:CheckBox19.Checked -eq $False})
 $Global:CheckBox19.Add_CheckStateChanged({$Global:CheckBox21.Enabled = $Global:CheckBox19.Checked -eq $False})
 $Global:CheckBox19.Add_CheckStateChanged({$Global:CheckBox22.Enabled = $Global:CheckBox19.Checked -eq $False})

 $Global:CheckBox20.Add_CheckStateChanged({$Global:CheckBox18.Enabled = $Global:CheckBox20.Checked -eq $False})
 $Global:CheckBox20.Add_CheckStateChanged({$Global:CheckBox19.Enabled = $Global:CheckBox20.Checked -eq $False})
 $Global:CheckBox20.Add_CheckStateChanged({$Global:CheckBox21.Enabled = $Global:CheckBox20.Checked -eq $False})
 $Global:CheckBox20.Add_CheckStateChanged({$Global:CheckBox22.Enabled = $Global:CheckBox20.Checked -eq $False})

 $Global:CheckBox21.Add_CheckStateChanged({$Global:CheckBox18.Enabled = $Global:CheckBox21.Checked -eq $False})
 $Global:CheckBox21.Add_CheckStateChanged({$Global:CheckBox19.Enabled = $Global:CheckBox21.Checked -eq $False})
 $Global:CheckBox21.Add_CheckStateChanged({$Global:CheckBox20.Enabled = $Global:CheckBox21.Checked -eq $False})
 $Global:CheckBox21.Add_CheckStateChanged({$Global:CheckBox22.Enabled = $Global:CheckBox21.Checked -eq $False})

 $Global:CheckBox22.Add_CheckStateChanged({$Global:CheckBox18.Enabled = $Global:CheckBox22.Checked -eq $False})
 $Global:CheckBox22.Add_CheckStateChanged({$Global:CheckBox19.Enabled = $Global:CheckBox22.Checked -eq $False})
 $Global:CheckBox22.Add_CheckStateChanged({$Global:CheckBox20.Enabled = $Global:CheckBox22.Checked -eq $False})
 $Global:CheckBox22.Add_CheckStateChanged({$Global:CheckBox21.Enabled = $Global:CheckBox22.Checked -eq $False})

 # Activate the form
 $Form.Add_Shown({$Form.Activate()})
 [void] $Form.ShowDialog()

}

function CONFIRM
{

 [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
 [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

 $ConfirmAction = ""

 #Set form size
 $Font = New-Object System.Drawing.Font("Times New Roman",12)
 $ConfirmForm = New-Object System.Windows.Forms.Form
 $ConfirmForm.width = 680
 $ConfirmForm.height = 130
 $ConfirmForm.WindowState = "Normal"
 $ConfirmForm.Text = ”Confirm”
 $ConfirmForm.AutoScroll = $False
 $ConfirmForm.Font = $Font
 $ConfirmForm.TopMost = $True
 $ConfirmForm.FormBorderStyle = "FixedDialog"
 $ConfirmForm.MaximizeBox = $False
 $ConfirmForm.MinimizeBox = $False
 $ConfirmForm.StartPosition = "CenterScreen"
 $ConfirmForm.Font = $Font

 $ConfirmCheckBox1 = new-object System.Windows.Forms.CheckBox
 $ConfirmCheckBox1.Location = new-object System.Drawing.Size(20,0)
 $ConfirmCheckBox1.Size = new-object System.Drawing.Size(660,50)
 $ConfirmCheckBox1.Text = "Correct permissions have been created on destination root folder that the data copy will inherit"
 $ConfirmCheckBox1.Checked = $False
 $ConfirmCheckBox1.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
 $ConfirmForm.Controls.Add($ConfirmCheckBox1)

 #Add a run button
 $ConfirmRunButton = new-object System.Windows.Forms.Button
 $ConfirmRunButton.Location = new-object System.Drawing.Size(540,50)
 $ConfirmRunButton.Size = new-object System.Drawing.Size(80,30)
 $ConfirmRunButton.Text = "Run"
 $ConfirmRunButton.Add_Click({
 $Global:ConfirmAction = "Run"
 $ConfirmForm.Close()})
 $ConfirmRunButton.Enabled = $False
 $ConfirmRunButton.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $ConfirmForm.Controls.Add($ConfirmRunButton)

 #Add a cancel button
 $ConfirmCancelButton = new-object System.Windows.Forms.Button
 $ConfirmCancelButton.Location = new-object System.Drawing.Size(470,50)
 $ConfirmCancelButton.Size = new-object System.Drawing.Size(60,30)
 $ConfirmCancelButton.Text = "Cancel"
 $ConfirmCancelButton.Add_Click({
 $Global:ConfirmAction = "Cancel"
 $ConfirmForm.Close()})
 $ConfirmCancelButton.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 $ConfirmForm.Controls.Add($ConfirmCancelButton)

 $ConfirmCheckBox1.Add_CheckStateChanged({$ConfirmRunButton.Enabled = $ConfirmCheckBox1.Checked})

 $ConfirmForm.Add_Shown({$ConfirmForm.Activate()})
 [void] $ConfirmForm.ShowDialog()
}

function RUN
{
 $Global:Option1 = ""
 $Global:Option2 = ""
 $Global:Option3 = ""
 $Global:Option4 = ""
 $Global:Option5 = ""
 $Global:Option6 = ""
 $Global:Option7 = ""
 $Global:Option8 = ""
 $Global:Option9 = ""
 $Global:Option10 = ""
 $Global:Option11 = ""
 $Global:Option12 = ""
 $Global:Option13 = ""
 $Global:Option14 = ""
 $Global:Option15 = ""
 $Global:Option16 = ""
 $Global:Option17 = ""
 $Global:Option18 = ""
 $Global:Option19 = ""
 $Global:Option20 = ""

 If ($Global:CheckBox1.Checked){$Global:Source = $Global:TextBox1.Text}
 If ($Global:CheckBox2.Checked){$Global:Destination = $Global:TextBox2.Text}
 If ($Global:CheckBox3.Checked){$Global:Option1 = "/S"}
 If ($Global:CheckBox4.Checked){$Global:Option2 = "/purge"}
 If ($Global:CheckBox5.Checked){$Global:Option3 = "/SDD"}
 If ($Global:CheckBox6.Checked){$Global:Option4 = "/D"}
 If ($Global:CheckBox7.Checked){$Global:Option5 = "/DE"}
 If ($Global:CheckBox8.Checked){$Global:Option6 = "/O"}
 If ($Global:CheckBox9.Checked){$Global:Option7 = "/secfix"}
 If ($Global:CheckBox10.Checked){$Global:Option8 = "/secforce"}
 If ($Global:CheckBox11.Checked){$Global:Option9 = "/nosec"}
 If ($Global:CheckBox12.Checked){$Global:Option10 = "/LG"}
 If ($Global:CheckBox13.Checked){$Global:Option11 = "/TH " + $Global:TextBox3.Text}
 If ($Global:CheckBox14.Checked){$Global:Option12 = "/R:" + $Global:TextBox4.Text}
 If ($Global:CheckBox15.Checked){$Global:Option13 = "/W:" + $Global:TextBox5.Text}
 If ($Global:CheckBox16.Checked){$Global:Option14 = "/C"}
 If ($Global:CheckBox17.Checked){$Global:Option15 = "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox18.Checked){$Global:Option16 = "/s /nosec /purge /sdd /de /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox19.Checked){$Global:Option17 = "/s /nosec /purge /sdd /de /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox20.Checked){$Global:Option18 = "/s /purge /sdd /de /o /secfix /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox21.Checked){$Global:Option19 = "/s /purge /sdd /de /o /secfix /lg /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}
 If ($Global:CheckBox22.Checked){$Global:Option20 = "/s /nosec /purge /sdd /de /th 32 /r:1 /w:1 /c " + "/LOG+:" + "c:\emcopy\copy_logs\" + ($Path = $Global:TextBox2.Text).Split("{\}")[-1] + "-LOG.txt"}

 $Global:RunAction = " " + $Global:Source + " " + $Global:Destination + " " + $Global:Option1 + " " + $Global:Option2 + " " + $Global:Option3 + " " + $Global:Option4 + " " + $Global:Option5 + " " + $Global:Option6 + " " + $Global:Option7 + " " + $Global:Option8 + " " + $Global:Option9 + " " + $Global:Option10 + " " + $Global:Option11 + " " + $Global:Option12 + " " + $Global:Option13 + " " + $Global:Option14 + " " + $Global:Option15 + " " + $Global:Option16 + " " + $Global:Option17 + " " + $Global:Option18 + " " + $Global:Option19 + " " + $Global:Option20
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")
 $Global:RunAction = $Global:RunAction.Replace(" "," ")

 Start-process .\EMCOPY64.exe $Global:RunAction
}

EMCOPY

IF ($Action -eq "Run"){CONFIRM}

IF ($ConfirmAction -eq "Run"){RUN}

SCCM 2012 R2 – Automatically populate and balance device collections with PowerShell 3.0

I had to recently throw a script together to address a need to automatically populate collections so that software could be deployed to a large environment in stages.

The designed solution was very interesting. Rather than deploying to 80,000 devices in a single hit, it was decided to deploy to deploy to 50 pilot users on Monday, 3,000 on Tuesday, 3,000 on Wednesday, 3,000 on Thursday and then all remaining machines on Friday.

To accomplish this, maintenance windows were introduced. This left one problem, how do you make sure the groups stay populated without someone having to manually count out 3,000 machine per group? It also got pointed out that a large hardware migration was imminent and trying to keep track of which ones were and were not in the group was going to get very complicated. Many ideas were thrown around including using exclude queries, but they all had the same downside… They required someone to regularly maintain these collections.

I turned to my favourite place for the solution, PowerShell.

This script is a very manual script and you will need to make sure that you have device collections to match the ones in the script. The only one that can be easily changed is the source collection. When I have time to sit down and refine this script I will post it here so that you can transfer it to your site easier

I am not going to go into details on each section of the script at this point but I will post it here if anyone wants something like this

###############################################################################
####                                                                       ####
####                                                                       ####
####                Maintenance Windows Collection Autofill                ####
####                            Version 1.0.1                              ####
####                       Created by: Liam Matthews                       ####
####                                                                       ####
####                                                                       ####
###############################################################################
 
################################Release notes##################################
####                                                                       ####
################################### 1.0.0 #####################################
#### - Initial release                                                     ####
####                                                                       ####
################################### 1.0.1 #####################################
#### - Changed source collection to be a variable                          ####
####                                                                       ####
###############################################################################
 
##User defined variables
$SCCMSiteCode = "A00"
$LogLocation = "E:\Scripts\"
$Component = "Maintenance Windows Collection Autofill"
$LogFileName = "Maintenance Windows Collection Autofill.log"
$SourceCollection = "Maintenance Windows - Scope"
 
##Machine Variables
$SCCMModule = $env:SMS_ADMIN_UI_PATH.Replace("\bin\i386","\bin\configurationmanager.psd1")
$CurrentUser = [Environment]::UserName
$SCCMDrive = $SCCMSiteCode + ":"
$StartTime = Get-Date -Format "HH:mm:ss.fff"
$StartDate = Get-Date -Format "MM-dd-yyyy"
$StartDateTime = Get-Date -Format "HHmmss"
$TZbias = (Get-WmiObject -Query "Select Bias from Win32_TimeZone").bias
 
##Remove log file if larger than 1MB
Get-Item 'E:\Scripts\Maintenance Windows Collection Autofill.log' | Where {$_.Length -gt 1048576} | Remove-Item
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "+++Starting new thread+++"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Checking for Configuration 1Manager PowerShell module"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get list of  current PowerShell modules
$CurrentModules = Get-Module
 
IF ($CurrentModules.Name -contains "ConfigurationManager")
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "2"
    $LogText = "Configuration Manager PowerShell module has already been imported. This is unexpected but script will continue"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "Configuration Manager PowerShell module is not currently present. Attempting import now"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Checking if SCCM PowerShell module is available for import
    IF (Test-Path $SCCMModule)
    {
        ##Import SCCM PowerShell module
        Import-Module $SCCMModule
 
        ##Get list of  current PowerShell modules
        $CurrentModules = Get-Module
             
        IF ($CurrentModules.Name -contains "ConfigurationManager")
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Configuration Manager PowerShell module has been imported successfully"
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
        }
        ELSE
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "3"
            $LogText = "Configuration Manager PowerShell module has failed to import. The script will now Exit"
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
             
            Exit
        }
    }
    ELSE
    {
        ##Output to Log
        $Time = Get-Date -Format "HH:mm:ss.fff"
        $Date = Get-Date -Format "MM-dd-yyyy"
        $Type = "3"
        $LogText = "Configuration Manager PowerShell module does not exist on the current machine. The script will now exit"
        $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
        Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
         
        Exit
    }
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Checking for SCCM Drive: $SCCMSiteCode"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get list of PowerShell drives
$PSDrives = Get-PSDrive | Select Name
 
IF ($PSDrives.Name -contains $SCCMSiteCode)
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "$SCCMSiteCode drive exists"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "Connecting to drive $SCCMSiteCode"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Connect to SCCM site
    CD $SCCMDrive
 
    ##Get current drive location
    $CurrentPSDrive = (get-location).Drive.Name
 
    IF ($CurrentPSDrive -eq "$SCCMSiteCode")
    {
        ##Output to Log
        $Time = Get-Date -Format "HH:mm:ss.fff"
        $Date = Get-Date -Format "MM-dd-yyyy"
        $Type = "1"
        $LogText = "Successfully connected to $SCCMSiteCode"
        $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
        Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
    }    
    ELSE
    {
        ##Output to Log
        $Time = Get-Date -Format "HH:mm:ss.fff"
        $Date = Get-Date -Format "MM-dd-yyyy"
        $Type = "3"
        $LogText = "Unable to connect to $SCCMSiteCode. The script will now exit"
        $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
        Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
        Exit
    }
}
ELSE
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "3"
    $LogText = "SCCM drive $SCCMSiteCode does not exist. Module may have issues or variable may be incorrect. The script will now exit"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    Exit
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Searching for SCCM CAS site"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get SCCM CAS Server
$CASServerName = Get-CMSite | Where {$_.Type -eq "4"}
$CasServerName = $CASServerName.ServerName
 
IF ($CASServerName -ne $Null)
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "Found SCCM CAS site $CASServerName"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "3"
    $LogText = "Unable to locate SCCM CAS site. The script will now exit"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    Exit
}
 
#Create blank array
$Devicestoadd = @()
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Identifying source collection"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get scope device collection
$MaintenanceWindowCollection = Get-CMDeviceCollection -Name $SourceCollection
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Getting list of devices in source collection"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##List devices in scope collection
$CollectionMembers = Get-WmiObject -ComputerName $CASServerName -Class SMS_CM_RES_COLL_$($MaintenanceWindowCollection.CollectionID) -Namespace root\sms\site_$SCCMSiteCode
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Identifying device membership"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
foreach ($CollectionMember in $CollectionMembers)
{
    ##List all collections that devices are members of
    $AllCollections = @()
    $ResID = (Get-CMDevice -Name $($CollectionMember.Name)).ResourceID
    $Collections = (Get-WmiObject -ComputerName $CASServerName -Class sms_fullcollectionmembership -Namespace root\sms\site_$SCCMSiteCode -Filter "ResourceID = '$($ResID)'").CollectionID
    foreach ($Collection in $Collections)
    {
        $DeviceCollectionMembership = Get-CMDeviceCollection -CollectionId $Collection | select Name, CollectionID
        $AllCollections += $DeviceCollectionMembership
    }
 
    IF ($AllCollections.name -Like "Maintenance Windows - Phase*")
    {
        ##Output to Log
        $Time = Get-Date -Format "HH:mm:ss.fff"
        $Date = Get-Date -Format "MM-dd-yyyy"
        $Type = "1"
        $LogText = $CollectionMember.Name + " is already part of a maintenance window collection"
        $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
        Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
    }
    ELSE
    {
        IF ($Devicestoadd.name -contains $CollectionMember.Name)
        {
        }
        ELSE
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = $CollectionMember.Name + " is not part of a maintenance window collection. Adding it to list to manage"
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
             
            ##Add device to array
            $Devicestoadd += $CollectionMember
        }
    }
 
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Getting list destination collections"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Identify destination collections
$Phase2Collection = Get-CMDeviceCollection -Name "Maintenance Windows - Phase 2"
$Phase3Collection = Get-CMDeviceCollection -Name "Maintenance Windows - Phase 3"
$Phase4Collection = Get-CMDeviceCollection -Name "Maintenance Windows - Phase 4"
$Phase5Collection = Get-CMDeviceCollection -Name "Maintenance Windows - Phase 5"
 
$Phase2CollectionCount = $Phase2Collection.LocalMemberCount
$Phase3CollectionCount = $Phase3Collection.LocalMemberCount
$Phase4CollectionCount = $Phase4Collection.LocalMemberCount
$Phase5CollectionCount = $Phase5Collection.LocalMemberCount
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = $Phase2Collection.Name + " currently has " + $Phase2CollectionCount + " Members"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = $Phase3Collection.Name + " currently has " + $Phase3CollectionCount + " Members"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = $Phase4Collection.Name + " currently has " + $Phase4CollectionCount + " Members"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = $Phase5Collection.Name + " currently has " + $Phase5CollectionCount + " Members"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
IF ($Devicestoadd.Count -eq "0")
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "No new devices to add to collections"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "Adding devices to maintenance window collections"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    foreach ($Devicetoadd in $Devicestoadd)
    {
        #Find collections to add device to
        IF (($Phase2CollectionCount -lt "3000") -and ($Phase2CollectionCount -le $Phase3CollectionCount) -and ($Phase2CollectionCount -le $Phase4CollectionCount))
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Adding device: " + $CollectionMember.Name + " to device collection: " + $Phase2Collection.Name
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
            #Add device to collections
            Add-CMDeviceCollectionDirectMembershipRule -CollectionName $Phase2Collection.Name -ResourceID $Devicetoadd.ResourceID
 
            #Manually increase collection count
            $Phase2CollectionCount += 1
 
        }
        ELSEIF (($Phase3CollectionCount -lt "3000") -and ($Phase3CollectionCount -le $Phase2CollectionCount) -and ($Phase3CollectionCount -le $Phase4CollectionCount))
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Adding device: " + $CollectionMember.Name + " to device collection: " + $Phase3Collection.Name
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
            #Add device to collections
            Add-CMDeviceCollectionDirectMembershipRule -CollectionName $Phase3Collection.Name -ResourceID $Devicetoadd.ResourceID
 
            #Manually increase collection count
            $Phase3CollectionCount += 1
        }
        ELSEIF (($Phase4CollectionCount -lt "3000") -and ($Phase4CollectionCount -le $Phase2CollectionCount) -and ($Phase4CollectionCount -le $Phase3CollectionCount))
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Adding device: " + $CollectionMember.Name + " to device collection: " + $Phase4Collection.Name
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
            #Add device to collections
            Add-CMDeviceCollectionDirectMembershipRule -CollectionName $Phase4Collection.Name -ResourceID $Devicetoadd.ResourceID
 
            ##Manually increase collection count
            $Phase4CollectionCount += 1
        }
        ELSEIF (($Phase4CollectionCount -ge "3000") -and ($Phase4CollectionCount -ge "3000") -and ($Phase4CollectionCount -ge "3000"))
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Adding device: " + $CollectionMember.Name + " to device collection: " + $Phase5Collection.Name
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
            #Add device to collections
            Add-CMDeviceCollectionDirectMembershipRule -CollectionName $Phase5Collection.Name -ResourceID $Devicetoadd.ResourceID
 
            ##Manually increase collection count
            $Phase5CollectionCount += 1
        }
    }
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = $Phase2Collection.Name + " now has " + $Phase2CollectionCount + " Members"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = $Phase3Collection.Name + " now has " + $Phase3CollectionCount + " Members"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = $Phase4Collection.Name + " now has " + $Phase4CollectionCount + " Members"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = $Phase5Collection.Name + " now has " + $Phase5CollectionCount + " Members "
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "+++Thread finished+++"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"

SCCM 2012 R2 – Expand SCCM Cache size using an Application

After coming across this several times, it has recently occurred to me that SCCM is missing what I would call a very important feature, The ability to manage the client cache size.

A reasonable person would assume that this option would be part of the client settings, however a quick look through will reveal that no administrative options exist.

There are many scripts out that that are designed to change the SCCM client cache size, these include “Program deployments” (Not really what you should be doing with SCCM now) and scripts that set the size and then write a registry value for detection. Both of these methods are far from what I would consider best practice and do not guarantee successful deployment or reporting, and also don’t take advantage of the full capabilities of SCCM

I have written two different scripts to accomplish this task. A PowerShell script, which is the preferred method, and a VB script, which is good for environments where PowerShell is not an option (such as sites with execution policy and signing restrictions)


PowerShell:

This script can be run with the argument DesiredCacheSizeMB, for example

SetSCCMCacheSize.ps1 -DesiredCacheSizeMB 10240


param($DesiredCacheSizeMB)
$DesiredCacheSizeBytes = $DesiredCacheSizeMB * 1024 * 1024
$ClientCache = Get-WmiObject -namespace root\ccm\SoftMgmtAgent -class CacheConfig
$CacheLocation = $ClientCache.Location
$CacheDriveName = $CacheLocation.Substring(0,2)
$CacheDrive = get-WmiObject win32_logicaldisk | Where {$_.DeviceID -eq $CacheDriveName}
$FreeSpaceBytes = $CacheDrive.FreeSpace

IF ($FreeSpaceBytes -gt $DesiredCacheSizeBytes)
{
    $ClientCache.size = $DesiredCacheSizeMB
    $ClientCache.InUse = "True"
    $ClientCache.Put()
    Exit 3010
}
ELSE
{
    Exit 112
}

You may notice that is is collecting wmi disk information. THis is to validate that there is sufficient disk space to expand the cache. You should set free disk space as a requirement within the deployment type

For the detection method, use the following script but making sure you change the value to suite what you are aiming for


$ClientCache = Get-WmiObject -namespace root\ccm\SoftMgmtAgent -class CacheConfig
$ClientCache.Size -eq "10240"

VBScript:

This is likely to work in more environments but Microsoft are starting to phase out vbscipt so this won’t work forever. I would also like to thank Nathan Grieve for helping me out with this one, it was a bit of a tricky one

Just like the PowerShell script, this will check for free disk space before attempting the change. This scipt has arguments but are put in without switched, for example
cscript.exe “SetSCCMCacheSize.vbs” 10240

On Error Resume Next
Dim UIResManager 
Dim Cache 
Dim CacheSizeMB
Dim CacheSizeBytes
Dim CacheDrive
Dim strComputer
Dim Str
strComputer="."
CacheSizeMB=WScript.Arguments(0)
CacheSizeKB=CacheSizeMB * 1024 * 1024

Set objWMIService1 = GetObject("winmgmts://" + strComputer + "/root/ccm/SoftMgmtAgent")
Set colItems = objWMIService1.ExecQuery("Select * from CacheConfig")

For Each objItem in colItems
 CacheDrive=(Left(objItem.Location,2))
Next

Set objWMIService2 = GetObject("winmgmts:")
Set objLogicalDisk = objWMIService2.Get("Win32_LogicalDisk.DeviceID=" + "'" + CacheDrive + "'")

IF objLogicalDisk.FreeSpace > CacheSizeKB THEN
 Set UIResManager = createobject ("UIResource.UIResourceMgr")
 Set Cache=UIResManager.GetCacheInfo()
 Cache.TotalSize=CacheSizeMB
 WScript.Quit(3010)
ELSE
 WScript.Quit(112)
END IF

Detection method, remember to change the detection size

strComputer = "."

Set objWMIService = GetObject("winmgmts://" & strComputer & "/root/ccm/SoftMgmtAgent")
Set colItems = objWMIService.ExecQuery("Select * from CacheConfig")

For Each objItem in colItems

 IF objItem.Size = 10240 Then
 WScript.StdOut.Write "Installed"
 WScript.Quit(0)
 ELSE
 'do nothing
 END IF
Next</pre>
<pre>

Both of these scripts will require a restart to take full effect so your SCCM deployment should reflect this