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

SCCM 2012 R2 – Exporting an application with PowerShell. Revision 1

I recently wrote a script for exporting an application from SCCM using PowerShell and another post about logging.

I thought the best thing to do would be to combine the two and provide a new script to export an application

Please note that this will export from a primary SCCM site as long as you have only got 1. If you have a CAS please modify the script from:


$PRIServerName = Get-CMSite | Where {$_.Type -eq "2"}


to


$PRIServerName = Get-CMSite | Where {$_.Type -eq "4"}


You can also change the variable names and log file details to match



##Script written by Liam Matthews
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Starting Script" -PercentComplete 10
 
##User defined variables
$SCCMSiteCode = "C01"
$ExportPathDirect = "E:\SCCM-Export\"
$LogLocation = "E:\Scripts\"
$LogFileName = "Application-Export.log"
 
##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
 
##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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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=`"$($LogFileName)`" 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 Primary site"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get SCCM CAS Server
$PRIServerName = Get-CMSite | Where {$_.Type -eq "2"}
$PRIServerName = $PRIServerName.ServerName
 
IF ($PRIServerName -ne $Null)
{
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Found SCCM CAS site $PRIServerName"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Unable to locate SCCM Primary site. The script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Searching for applications" -PercentComplete 30
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Searching for applications"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get applicaion list via WMI
$Applications = get-wmiobject -ComputerName $PRIServerName -Namespace Root\SMS\Site_$SCCMSiteCode -Class "SMS_Application" | Select -unique LocalizedDisplayName | sort LocalizedDisplayName
$ApplicationsCount = $Applications.Count
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Found $ApplicationsCount applications"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" 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 = "Requesting use input"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Application Import Selection Form
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
 
$form1 = New-Object System.Windows.Forms.Form
$form1.Text = "Application Import"
$form1.Size = New-Object System.Drawing.Size(425,380)
$form1.StartPosition = "CenterScreen"
 
$OKButton1 = New-Object System.Windows.Forms.Button
$OKButton1.Location = New-Object System.Drawing.Point(300,325)
$OKButton1.Size = New-Object System.Drawing.Size(75,23)
$OKButton1.Text = "OK"
$OKButton1.DialogResult = [System.Windows.Forms.DialogResult]::OK
$OKButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.AcceptButton = $OKButton1
$form1.Controls.Add($OKButton1)
 
$CancelButton1 = New-Object System.Windows.Forms.Button
$CancelButton1.Location = New-Object System.Drawing.Point(225,325)
$CancelButton1.Size = New-Object System.Drawing.Size(75,23)
$CancelButton1.Text = "Cancel"
$CancelButton1.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$CancelButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.CancelButton = $CancelButton1
$form1.Controls.Add($CancelButton1)
 
$label1 = New-Object System.Windows.Forms.Label
$label1.Location = New-Object System.Drawing.Point(10,5)
$label1.Size = New-Object System.Drawing.Size(280,20)
$label1.Text = "Select an application to import"
$form1.Controls.Add($label1)
 
$listBox1 = New-Object System.Windows.Forms.Listbox
$listBox1.Location = New-Object System.Drawing.Size(10,30)
$listBox1.Width = 400
$listBox1.Height = 296
$listBox1.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 
IF ($Applications -eq $Null)
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "No Applications were found. Script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
##Add items to form
foreach($Application in $Applications)
{
[void] $ListBox1.Items.Add($Application.LocalizedDisplayName)
}
$form1.Controls.Add($listBox1)
$form1.Topmost = $True
$result1 = $form1.ShowDialog()
IF ($result1 -eq [System.Windows.Forms.DialogResult]::OK)
{
$SelectedApplication = $listBox1.SelectedItems
$SelectedApplication = $SelectedApplication[0]
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "User selected $SelectedApplication"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" 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 = "User cancelled input request. The script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
}
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Exporting $SelectedApplication" -PercentComplete 60
 
##Clearing error list
$Error.Clear()
 
$ExportPath = $ExportPathDirect + $SelectedApplication + ".zip"
 
IF (Test-Path $ExportPath)
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "$SelectedApplication has already been exported. Script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" 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 = "1"
$LogText = "Attempting application export"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
Export-CMApplication -IgnoreRelated -Path $ExportPath -Name $SelectedApplication
 
IF ($Error.Count -ge "1")
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Error: " + $Error[0].Exception
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" 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 = "3"
$LogText = "Something appears to have gone wrong. Script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" 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 = "1"
$LogText = "Successfully exported $SelectedApplication"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
}
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Finalizing" -PercentComplete 90
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Retrieving deployment type information"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Rename exported folders to deployment type name
$DeploymentTypes = Get-CMDeploymentType -ApplicationName $SelectedApplication
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Attempting to rename folders"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Modify deployment type folders
foreach ($DeploymentType in $DeploymentTypes)
{
$Error.Clear()
$OldContentName = $ExportPathDirect + $SelectedApplication + "_files\" + $DeploymentType.ContentId
$NewContentName = $DeploymentType.LocalizedDisplayName
$NewContentName = $NewContentName.Replace("|","")
$NewContentName = $NewContentName.Replace("\","")
$NewContentName = $NewContentName.Replace("/","")
$NewContentName = $NewContentName.Replace("?","")
$NewContentName = $NewContentName.Replace("*","")
$NewContentName = $NewContentName.Replace("<","")
$NewContentName = $NewContentName.Replace(">","")
Rename-Item -NewName $NewContentName -Path $OldContentName
 
$NewContentPath = $ExportPathDirect + $SelectedApplication + "_files\" + $NewContentName
 
IF (Test-Path $NewContentPath)
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Successfully renamed " + "'" + $OldContentName + "' to '" + $NewContentPath + "'"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" 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 = "Error: " + $Error[0].Exception
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" 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 = "3"
$LogText = "Something appears to have gone wrong. Script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
}
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Export of $SelectedApplication Completed" -PercentComplete 100
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Application export script successfully completed"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" 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 = "+++Finished current thread+++"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"


Make sure that the export destination folder exists prior to starting the script

As usual, if you have any issues or questions please post them in the comments section below

Windows PowerShell 3.0 – Outputting to a log for CMTrace

Recently I have been writing some very long PowerShell scripts for many different purposes but I ran into a common problem… How to deal with error handling.

There are many options available and the Windows PowerShell inbuilt -OnError function works fine, but I wanted something more, and more to the point, the customer wanted more. With this in mind I decided that the best option was going to be to export to a log file in a format that was readable with the CMTrace tool that comes packaged with SCCM.

Using this method means that I can output all information, including success and failures for each task that I run. It also means that as the script is running, users can monitor the progress in real-time.

 

The first thing you need to do is place the following variables somewhere at the top of your script

$CurrentUser = [Environment]::UserName
$StartDateTime = Get-Date -Format “HHmmss”
$TZbias = (Get-WmiObject -Query “Select Bias from Win32_TimeZone”).bias
$Component = “Application Export”
$LogFile = “C:\Logs\My Script.Log”

$CurrentUser will get the currently logged on username to use for the “File” column. This is not required but could (and usually is) set to the currently running script by way of the following line. I have chosen this method because my output file is the same as the script name making this useless, and many people will be logged on to the same server running the same script so i would like to be able to identify which user is running the script

$CurrentFile = $MyInvocation.MyCommand.Name

$StartDateTime fetches the current date and time as of the script launch time, this is going to be used for the “Thread” column. This is not required and is a personal choice. You could put many other things here, like the current username, script name, or just about anything else you want.

$TZbias gets the current timezone. This is required for the “Time” column. Without this the time will not be recognized and the log will not be in the correct format for CMTrace.exe.

$Component can be defined to anything you cant. I have it set to the current script. Have a play around with some of these variables and see what you get as the output

##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”

Each time you would like to output a line to the log you will need to paste the whole text above. Let’s go through now and break it down to see what is going on

$Time = Get-Date -Format “HH:mm:ss.fff”
$Date = Get-Date -Format “MM-dd-yyyy”

Both of these fetch the date or the time and bind it to a variable. This will need to be set each time to ensure that the time stamp for your log matches the time of the task running

$Type = “1”

This line will define the formatting of the line. “1” means information and will be a plain line with a white background. “2” means warning and will have a yellow background. “3” means error and will have a red background so set accordingly

$LogText = “+++Starting new thread+++”

This is the main meat of the script. This is where you can put all of your information about what is going on. I will always start and finish a script with a clear maker such as this.  Setting this as a variable and not putting it directly into the next line means that you ave more flexibility to change it, something like the following line

$LogText = “The following function has failed:” + $Function

This variable can also be set to a function (such as Get-Item) and will correctly display multiple lines in CMTrace

$LogOutput = “<![LOG[$($LogText)]LOG]!><time=`”$($Time)+$($TZBias)`” date=`”$($Date)`” component=`”$($Component)`” context=`”$($Context)`” type=`”$($Type)`” thread=`”$($StartDateTime)`” file=`”$($CurrentUser)`”>”

This is something that you should not need to alter. All of the data here that you could possibly want to modify is defined as variables. Just in case it is not clear, this is a single line. WordPress has limitations

Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath “$LogFile”

This is where we output all of our hard work to a file. The “Append” function will place all text at the end of the file. “NoClobber” prevents the script from overwriting the log file with a new one each time you run this command

If you have any questions feel free to write something in the comments below

SCCM 2012 R2 – Exporting an application with PowerShell

Recently i have been doing some work for a client that likes to segregate their environment for test and production. While this is great for many reasons, one of the downsides is having to export an application and then import it into the next environment.

In this post i will be showing a PowerShell script that I developed to export applications from SCCM 2012 R2 to a predetermined folder. I will also do my best to explain each step of the script. As usual this script should work on SCCM 2012 and SCCM 2012 SP1 but I have only tested it on SCCM 2012 R2


##Script written by Liam Matthews 2015

##Variable list
$SCCMModule = “C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1”
$SCCMSiteCode = “001”
$SCCMDrive = $SCCMSiteCode + “:”
$SCCMServer = “SCCM2012.liammatthewsit.com”
$ExportPathDirect = “E:\SCCM Export\”

##Progress bar
Write-Progress -Activity “Application Export” -Status “Starting Script” -PercentComplete 10

##Import SCCM PowerShell module
Import-Module $SCCMModule

##Connect to SCCM site
CD $SCCMDrive

##Pause to show previous display message
Start-Sleep 5

##Progress bar
Write-Progress -Activity “Application Export” -Status “Searching for applications” -PercentComplete 30

##Get applicaion list via WMI
$Applications = Get-WMIObject -ComputerName $SCCMServer -Namespace Root\SMS\Site_$SCCMSiteCode -Class “SMS_Application” | Select -unique LocalizedDisplayName | sort LocalizedDisplayName

##Application Import Selection Form
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form1 = New-Object System.Windows.Forms.Form
$form1.Text = “Application Import”
$form1.Size = New-Object System.Drawing.Size(425,380)
$form1.StartPosition = “CenterScreen”

$OKButton1 = New-Object System.Windows.Forms.Button
$OKButton1.Location = New-Object System.Drawing.Point(300,325)
$OKButton1.Size = New-Object System.Drawing.Size(75,23)
$OKButton1.Text = “OK”
$OKButton1.DialogResult = [System.Windows.Forms.DialogResult]::OK
$OKButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.AcceptButton = $OKButton1
$form1.Controls.Add($OKButton1)

$CancelButton1 = New-Object System.Windows.Forms.Button
$CancelButton1.Location = New-Object System.Drawing.Point(225,325)
$CancelButton1.Size = New-Object System.Drawing.Size(75,23)
$CancelButton1.Text = “Cancel”
$CancelButton1.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$CancelButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.CancelButton = $CancelButton1
$form1.Controls.Add($CancelButton1)

$label1 = New-Object System.Windows.Forms.Label
$label1.Location = New-Object System.Drawing.Point(10,5)
$label1.Size = New-Object System.Drawing.Size(280,20)
$label1.Text = “Select an application to import”
$form1.Controls.Add($label1)

$listBox1 = New-Object System.Windows.Forms.Listbox
$listBox1.Location = New-Object System.Drawing.Size(10,30)
$listBox1.Width = 400
$listBox1.Height = 296
$listBox1.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right

##Add items to form
foreach($Application in $Applications)
{
[void] $ListBox1.Items.Add($Application.LocalizedDisplayName)
}

$form1.Controls.Add($listBox1)
$form1.Topmost = $True
$result1 = $form1.ShowDialog()
if ($result1 -eq [System.Windows.Forms.DialogResult]::OK)
{
$SelectedApplication = $listBox1.SelectedItems
$SelectedApplication = $SelectedApplication[0]
}
else
{
exit
}

##Progress bar
Write-Progress -Activity “Application Export” -Status “Exporting $SelectedApplication” -PercentComplete 60

##Export selected application
$ExportPath = $ExportPathDirect + $SelectedApplication + “.zip”
Export-CMApplication -IgnoreRelated -Path $ExportPath -Name $SelectedApplication

##Progress bar
Write-Progress -Activity “Application Export” -Status “Finalizing” -PercentComplete 90

##Rename exported folders to deployment type name
$DeploymentTypes = Get-CMDeploymentType -ApplicationName $SelectedApplication
foreach ($DeploymentType in $DeploymentTypes)
{
$OldContentName = $ExportPathDirect + $SelectedApplication + “_files\” + $DeploymentType.ContentId
$NewContentName = $DeploymentType.LocalizedDisplayName
$NewContentName = $NewContentName.Replace(“|”,””)
$NewContentName = $NewContentName.Replace(“\”,””)
$NewContentName = $NewContentName.Replace(“/”,””)
$NewContentName = $NewContentName.Replace(“?”,””)
$NewContentName = $NewContentName.Replace(“*”,””)
$NewContentName = $NewContentName.Replace(“<“,””)
$NewContentName = $NewContentName.Replace(“>”,””)
Rename-Item -NewName $NewContentName -Path $OldContentName
}

##Progress bar
Write-Progress -Activity “Application Export” -Status “Export of $SelectedApplication Completed” -PercentComplete 100

##Pause to show previous display message
Start-Sleep 5


OK, so there is the script in its entirety, lets go ahead and start breaking this down

I will be skipping over anything to do with the progress bar and anything that has Start-Sleep as these are both for the user to see


##Variable list

$SCCMModule = “C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1”
$SCCMSiteCode = “001”
$SCCMDrive = $SCCMSiteCode + “:”
$SCCMServer = “SCCM2012.liammatthewsit.com”
$ExportPathDirect = “E:\SCCM Export\”

In this section we are defining the variables that we will be using in the script. Not all variables can be created at this stage because they may rely on information that we do not currently have, anything that does not will be listed here so that it is easy to find


##Import SCCM PowerShell module
Import-Module $SCCMModule

##Connect to SCCM site
CD $SCCMDrive

These commands import the SCCM module and then connect to the appropriate drive (Which would be your site code)


##Get applicaion list via WMI
$Applications = Get-WMIObject -ComputerName $SCCMServer -Namespace Root\SMS\Site_$SCCMSiteCode -Class “SMS_Application” | Select -unique LocalizedDisplayName | sort LocalizedDisplayName

This will connect to WMI on the SCCM server and fetch a list of all applications that currently exist, note that it filters out duplicated via “Select -unique”. Within WMI there will be several versions of an application listed, this is because each revision will be displayed as though it is a different application

Some people might be asking “Why are you not using the module command to get a list of applications?” I have used WMI because it is a faster query than “Get-CMApplication”. This may not be a big issue if you are using this in a lab or you only have a few dozen applications, but when the number of applications exceeds 500 then you might as well take your lunch break while it runs

If you are keen you can change this line to the following (you will also be able to remove the $SCCMServer variable

$Applications = Get-CMApplication | Select LocalizedDisplayName | sort LocalizedDisplayName


##Application Import Selection Form
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form1 = New-Object System.Windows.Forms.Form
$form1.Text = “Application Import”
$form1.Size = New-Object System.Drawing.Size(425,380)
$form1.StartPosition = “CenterScreen”

$OKButton1 = New-Object System.Windows.Forms.Button
$OKButton1.Location = New-Object System.Drawing.Point(300,325)
$OKButton1.Size = New-Object System.Drawing.Size(75,23)
$OKButton1.Text = “OK”
$OKButton1.DialogResult = [System.Windows.Forms.DialogResult]::OK
$OKButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.AcceptButton = $OKButton1
$form1.Controls.Add($OKButton1)

$CancelButton1 = New-Object System.Windows.Forms.Button
$CancelButton1.Location = New-Object System.Drawing.Point(225,325)
$CancelButton1.Size = New-Object System.Drawing.Size(75,23)
$CancelButton1.Text = “Cancel”
$CancelButton1.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$CancelButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.CancelButton = $CancelButton1
$form1.Controls.Add($CancelButton1)

$label1 = New-Object System.Windows.Forms.Label
$label1.Location = New-Object System.Drawing.Point(10,5)
$label1.Size = New-Object System.Drawing.Size(280,20)
$label1.Text = “Select an application to import”
$form1.Controls.Add($label1)

$listBox1 = New-Object System.Windows.Forms.Listbox
$listBox1.Location = New-Object System.Drawing.Size(10,30)
$listBox1.Width = 400
$listBox1.Height = 296
$listBox1.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right

This is a long one, I am not going to go through each line one at a time. this section creates a list box. The bulk of this code was taken from the Microsoft Technet article on list boxes, however is was modified to make it expandable when dragged


##Add items to form
foreach($Application in $Applications)
{
[void] $ListBox1.Items.Add($Application.LocalizedDisplayName)
}

$form1.Controls.Add($listBox1)
$form1.Topmost = $True
$result1 = $form1.ShowDialog()
if ($result1 -eq [System.Windows.Forms.DialogResult]::OK)
{
$SelectedApplication = $listBox1.SelectedItems
$SelectedApplication = $SelectedApplication[0]
}
else
{
exit
}

Here we are feting the data from the $Applications variable that we specified earlier and turning each entry into an item in the list box. The bottom half of this code also specifies the action that is taken when the OK button is pressed. In this case it is to assign the selected item to a variable that we can use later


##Export selected application
$ExportPath = $ExportPathDirect + $SelectedApplication + “.zip”
Export-CMApplication -IgnoreRelated -Path $ExportPath -Name $SelectedApplication

This is where all the magic happens. Here we are using all the variables that we have created to export the application to the folder the we specified at the top of the script


##Rename exported folders to deployment type name
$DeploymentTypes = Get-CMDeploymentType -ApplicationName $SelectedApplication
foreach ($DeploymentType in $DeploymentTypes)
{
$OldContentName = $ExportPathDirect + $SelectedApplication + “_files\” + $DeploymentType.ContentId
$NewContentName = $DeploymentType.LocalizedDisplayName
$NewContentName = $NewContentName.Replace(“|”,””)
$NewContentName = $NewContentName.Replace(“\”,””)
$NewContentName = $NewContentName.Replace(“/”,””)
$NewContentName = $NewContentName.Replace(“?”,””)
$NewContentName = $NewContentName.Replace(“*”,””)
$NewContentName = $NewContentName.Replace(“<“,””)
$NewContentName = $NewContentName.Replace(“>”,””)
Rename-Item -NewName $NewContentName -Path $OldContentName
}

This section is entirely optional, however, when the application is exported is puts the source files in a folder which is named with the Deployment Type GUID. When we are using a script to import this we will run into several problems because it will have no idea which folder belongs to which Deployment Type

This part of the script will rename each folder to the Deployment Type name so that it is easier to reference this later. When using this script though you will need to make sure that every Deployment Type name in your environment is unique, otherwise you are going to run into duplicate folder problems


Hopefully this script will prove useful and you have a good understanding of how it functions so that you can modify to suite your environment. If you have any questions or don’t understand something in the script then please comment

Stay tuned to the import script that will be following shortly