MasterPromptPS.ps1
<# .SYNOPSIS Tag Servers with Inventory data. .DESCRIPTION Tag a local system or remote server or servers inventory data. .PARAMETER SettingsFile Specifies the json file with the settings and menu definition. .PARAMETER HideProgressBar Hide the SCCM task sequence progress bar. .PARAMETER Test Test GUI generation only, do not attempt to create Task Sequence variables. .LINK http://www.1st-technologies.com/library/masterpromptps .INPUTS None. MasterPromptPS does not accept object inputs. .OUTPUTS None, MasterPromptPS does not output any objects. .EXAMPLE C:\ PS> .\MasterPromptPS Display a prompt as defiend by the MasterPromptPS.json file (default) located in the currect directory, store results in task sequence variables .EXAMPLE C:\ PS> .\MasterPromptPS -HideProgressBar Hide the SCCM progress bar, then display a prompt as defiend by the MasterPromptPS.json file (default) located in the currect directory, store results in task sequence variables .EXAMPLE C:\ PS> .\MasterPromptPS -SettingsFile .\server.json Display a prompt as defiend by .\server.json, store results in task sequence variables .NOTES Name : MasterPromptPS Version : 0.1.3 Author : Alfredo Blanco Date : November 5, 2021 Copyright : (c) 2021 1st Technologies, Inc. All Rights Reserved Changes : 0.1.0 - Initial Release 0.1.1 - Add custom validation 0.1.2 - Add cascade field and data file version validation Add footer field Remove Cancel button, bind the "Escape" only for -Test and remove control box "X" button 0.1.3 - Add support for multiple radio button groups, misc. improvements #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage="Specifies the settings file.")] [ValidateScript({ If(Test-Path -Path $_ -PathType Leaf){ $true }else{ Throw "Settings file not found: $_" } })] [ValidateNotNullOrEmpty()] [string]$SettingsFile = "$PSScriptRoot\MasterPromptPS.json", [Parameter(Mandatory = $false, HelpMessage="Hide the SCCM progress bar.")] [switch]$HideProgressBar, [Parameter(Mandatory = $false, HelpMessage="Test GUI generation only, do not attempt to create Task Sequence variables.")] [switch]$Test ) Begin{ $Version = "0.1.3" Write-Verbose -Message "Defining Functions..." Function WMIQuery{ Param( [Parameter(Mandatory = $true, HelpMessage="A valid WQL query.")] [ValidateNotNullOrEmpty()] [string]$query, [Parameter(Mandatory = $false, HelpMessage="Specifies the WMI namespace to query. Default [root\cimv2]")] [ValidateNotNullOrEmpty()] [string]$namespace="root\cimv2" ) $class = Get-WmiObject -Query $query -Namespace $namespace If ($class.Properties.IsArray){ return $class.$($class.properties.name)[0] }else{ return $class.$($class.properties.name) } } Function SetTSVariable{ Param( [Parameter(Mandatory = $True, HelpMessage = "Specify the variable name")] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(Mandatory = $True, HelpMessage = "Specify the variable value")] [ValidateNotNullOrEmpty()] [string]$Value ) $smsts = New-Object -ComObject Microsoft.SMS.TSEnvironment $smsts[$Name] = $Value } Write-Verbose -Message "Reading Settings file..." $Settings = Get-Content -Path $SettingsFile | ConvertFrom-Json If($Settings.__version__ -ne $Version){ Throw "Invalid Settings file version $($Settings.__version__)." } Write-Verbose -Message "Loading Assemblies..." Try{ [void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") }Catch{ Throw("Error loading assembly: {0}" -f $($_.exception.message)).ToString().Trim() } Function PopulateCascade($mCB, $mDCB, $mCascade){ $mCB.items.Clear() $cascadeitems = $($($Settings.fields | Where-Object{$_.name -eq $mCascade}).options.$($mDCB.SelectedItem.Value)).psobject.properties $objects = @() Foreach ($selectoption in $cascadeitems){ $obj = new-object System.Object $obj | add-member -type NoteProperty -name Label -value $selectoption.value $obj | add-member -type NoteProperty -name Value -value $selectoption.name $objects += $obj } $mCB.items.addrange($objects) $mCB.DisplayMember = "label" $mCB.ValueMember = "Value" $mCB.Items.Insert(0, "Select an option") $mCB.SelectedIndex = 0 $mCB.Enabled = $True } } Process{ If($HideProgressBar){ $smsui = New-Object -ComObject "Microsoft.SMS.TSProgressUI" $smsui.CloseProgressDialog() } Write-Verbose -Message "Drawing form..." $objForm = New-Object System.Windows.Forms.Form $objForm.Text = $Settings.title $objForm.Size = [system.drawing.size]::new($Settings.width, $Settings.height) $objForm.StartPosition = "CenterScreen" $objIicon = [System.Drawing.Icon]::new("$PSScriptRoot\images\favicon.ico") $objForm.Icon = $objIicon $objForm.BackgroundImage = $objIimage $objForm.BackgroundImageLayout = "None" $objForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog $objForm.MaximizeBox = $false $objForm.MinimizeBox = $false $objForm.ControlBox = $false Write-Verbose -Message "Defining fonts..." $LabelFont = [System.Drawing.Font]::new("Arial",10,[System.Drawing.FontStyle]::Bold) $FieldFont = [System.Drawing.Font]::new("Arial",10,[System.Drawing.FontStyle]::Regular) $HeaderFont = [System.Drawing.Font]::new("Arial",18,[System.Drawing.FontStyle]::Bold) Write-Verbose -Message "Wireing keyboard hooks..." $objForm.KeyPreview = $True $objForm.Add_KeyDown({ if ($_.KeyCode -eq "Enter"){ $OKButton.PerformClick() } }) If($Test){ $objForm.Add_KeyDown({ if ($_.KeyCode -eq "Escape"){ $objForm.Close() } }) } $objIimage = [system.drawing.image]::FromFile("$PSScriptRoot\images\logo.png") $oPictureBox = New-Object Windows.Forms.PictureBox $oPictureBox.Image = $objIimage $oPictureBox.Width = $oPictureBox.AutoSize = $true $oPictureBox.Location = [system.drawing.size]::new(8,17) $objHeaderPanel = New-Object System.Windows.Forms.Panel $objHeaderPanel.BackColor = [System.Drawing.ColorTranslator]::FromHtml($Settings.headercolor) $objHeaderPanel.Dock = [System.Windows.Forms.DockStyle]::Top $objHeaderPanel.SendToBack() $objHeaderLabel = New-Object System.Windows.Forms.Label $objHeaderLabel.Location = [system.drawing.size]::new(10,5) $objHeaderLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter $objHeaderLabel.Size = [System.Drawing.Size]::new($Settings.width, 20) $objHeaderLabel.Text = $Settings.header $objHeaderLabel.Font = $HeaderFont $objHeaderLabel.BackColor = "Transparent" $objHeaderLabel.ForeColor = "White" $objHeaderPanel.Controls.Add($oPictureBox) $objHeaderPanel.Controls.Add($objHeaderLabel) $objForm.Controls.Add($objHeaderPanel) ForEach ($Field in $Settings.fields){ Write-Verbose -Message "Generating $($Field.type) field..." Switch ($Field.type){ {($_ -eq "info") -or ($_ -eq "text") -or ($_ -eq "password") -or ($_ -eq "radio") -or ($_ -eq "select") -or ($_ -eq "check") -or ($_ -eq "cascade")} { $Settings.rowy += $Settings.rowheight Set-Variable -Name "l$($Field.name)" -Value $(New-Object -TypeName System.Windows.Forms.Label) Invoke-Expression('$l' + $Field.name + '.Text = "' + $Field.label + '"') Invoke-Expression('$l' + $Field.name + '.AutoSize = $true') Invoke-Expression('$l' + $Field.name + '.Location = [system.drawing.size]::new(' + $Settings.labelcolumnX + ', ' + $Settings.rowy + ')') Invoke-Expression('$l' + $Field.name + '.Font = $LabelFont') Invoke-Expression('$objForm.Controls.Add($l' + $Field.name + ')') } "info" { Set-Variable -Name "f$($Field.name)" -Value $(New-Object -TypeName System.Windows.Forms.Label) Invoke-Expression('$f' + $Field.name + '.Text = WMIQuery(' + [char]34 + $Field.query + [char]34 +')') Invoke-Expression('$f' + $Field.name + '.AutoSize = $true') Invoke-Expression('$objForm.Controls.Add($f' + $Field.name + ')') } "text" { Set-Variable -Name "f$($Field.name)" -Value $(New-Object -TypeName System.Windows.Forms.TextBox) Invoke-Expression('$f' + $Field.name + '.MaxLength = ' + $Field.maxLength) If($Field.default){ Invoke-Expression('$f' + $Field.name + '.Text = WMIQuery(' + [char]34 + $Field.default + [char]34 +')') } Invoke-Expression('$objForm.Controls.Add($f' + $Field.name + ')') } "password" { Set-Variable -Name "f$($Field.name)" -Value $(New-Object -TypeName System.Windows.Forms.TextBox) Invoke-Expression('$f' + $Field.name + '.PasswordChar = "*"') Invoke-Expression('$objForm.Controls.Add($f' + $Field.name + ')') } "radio" { Set-Variable -Name "f$($Field.name)" -Value $(New-Object -TypeName System.Windows.Forms.GroupBox) Invoke-Expression('$f' + $Field.name + '.Size = [system.drawing.size]::new(' + $Settings.textboxsize + ', ' + ($Settings.rowheight * 2) + ')') Invoke-Expression('$f' + $Field.name + '.Location = [system.drawing.size]::new(' + $Settings.fieldcolumnX + ', ' + ($Settings.rowy - 2) + ')') Foreach ($radiooption in $Field.options.psobject.properties){ Write-Verbose -Message "Generating Radio Button Option: $($radiooption.name)" Set-Variable -Name "f$($Field.name)_$($radiooption.name)" -Value $(New-Object -TypeName System.Windows.Forms.RadioButton) Invoke-Expression('$f' + $Field.name + '_' + $radiooption.name + '.dock = [System.Windows.Forms.DockStyle]::Left') Invoke-Expression('$f' + $Field.name + '_' + $radiooption.name + '.Text = "' + $radiooption.value + '"') Invoke-Expression('$f' + $Field.name + '_' + $radiooption.name + '.Font = $FieldFont') Invoke-Expression('$f' + $Field.name + '.Controls.Add($f' + $Field.name + "_" + $radiooption.name + ')') If($Field.default -eq $radiooption.name){ Invoke-Expression('$f' + $Field.name + '_' + $radiooption.name + '.Checked = $True') } } $Settings.rowy += ($Settings.rowheight * 1.3) Invoke-Expression('$objForm.Controls.Add($f' + $Field.name + ')') } "check" { Set-Variable -Name "f$($Field.name)" -Value $(New-Object -TypeName System.Windows.Forms.CheckBox) Invoke-Expression('$f' + $Field.name + '.Autosize = $true') Invoke-Expression('$f' + $Field.name + '.Location = [system.drawing.size]::new(' + $Settings.fieldcolumnX + ', ' + ($Settings.rowy + 4) + ')') Invoke-Expression('$objForm.Controls.Add($f' + $Field.name + ')') If($Field.checked){ Invoke-Expression('$f' + $Field.name + '.Checked = $True') } } "select" { Set-Variable -Name "f$($Field.name)" -Value $(New-Object -TypeName System.Windows.Forms.Combobox) Invoke-Expression('$f' + $Field.name + '.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList') $objects = @() Foreach ($selectoption in $Field.options.psobject.properties){ $obj = new-object System.Object $obj | add-member -type NoteProperty -name Label -value $selectoption.value $obj | add-member -type NoteProperty -name Value -value $selectoption.name $objects += $obj } Invoke-Expression('$f' + $Field.name + '.items.addrange($objects)') Invoke-Expression('$f' + $Field.name + '.DisplayMember = "Label"') Invoke-Expression('$f' + $Field.name + '.ValueMember = "Value"') Invoke-Expression('$f' + $Field.name + '.Items.Insert(0, "Select an option")') Invoke-Expression('$f' + $Field.name + '.SelectedIndex = 0') Invoke-Expression('$objForm.Controls.Add($f' + $Field.name + ')') } "cascade" { Invoke-Expression('$f' + $Field.name + ' = New-Object System.Windows.Forms.Combobox') Invoke-Expression('$f' + $Field.name + '.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList') Invoke-Expression('$f' + $Field.name + '.Enabled = $False') Invoke-Expression('$f' + $Field.dependsOn + '.add_SelectedIndexChanged{PopulateCascade $f' + $Field.name + ' $f' + $Field.dependsOn + ' ' + $Field.name + '}') Invoke-Expression('$f' + $Field.name + '.Items.Insert(0, "Select an option")') Invoke-Expression('$f' + $Field.name + '.SelectedIndex = 0') Invoke-Expression('$objForm.Controls.Add($f' + $Field.name + ')') } {($_ -eq "text") -or ($_ -eq "password") -or ($_ -eq "select") -or ($_ -eq "cascade")} { Invoke-Expression('$f' + $Field.name + '.Size = [system.drawing.size]::new(' + $Settings.textboxsize + ', 20)') } {($_ -eq "info") -or ($_ -eq "text") -or ($_ -eq "password") -or ($_ -eq "select") -or ($_ -eq "cascade")} { Invoke-Expression('$f' + $Field.name + '.Font = $FieldFont') Invoke-Expression('$f' + $Field.name + '.Location = [system.drawing.size]::new(' + $Settings.fieldcolumnX + ', ' + $Settings.rowy + ')') } Default {throw "Unknown prompt rule $($Field.type) in $SettingsFile"} } } $objForm.Controls | ForEach-Object{ Write-Verbose -Message "=== $_ ===" } $Settings.rowy += $Settings.rowheight + 20 $chkDebug = New-Object System.Windows.Forms.CheckBox $chkDebug.Autosize = $true $chkDebug.BringToFront() $chkDebug.Location = [System.Drawing.Size]::new($settings.width - 35, 105) $chkDebug.BringToFront() $objForm.Controls.Add($chkDebug) $okButtonX = ($objForm.Width/2)-55 $OKButton = New-Object System.Windows.Forms.Button $OKButton.Location = [system.drawing.size]::new($okButtonX, $Settings.rowy) $OKButton.Size = [system.drawing.size]::new(100, 25) $OKButton.Text = "OK" $OKButton.Add_Click{ $valid = $true $cvalid = $true ForEach ($Field in $Settings.fields){ Switch ($Field.type){ {($_ -eq "text") -or ($_ -eq "password")}{ If($Field.required -eq $true){ Invoke-Expression('if ($f' + $Field.name + '.text.length -eq 0){$valid = $false; $f' + $Field.name + '.Backcolor = [System.Drawing.ColorTranslator]::FromHtml("' + $Settings.errorcolor + '")}else{$f' + $Field.name + '.ResetBackColor()}') If(Test-Path -Path "$PSScriptroot\customvalidators\$($Field.name).ps1"){ $cvalid=(. .\customvalidators\$($Field.name).ps1 -Value (Invoke-Expression('$f' + $Field.name + '.text'))) } } } {($_ -eq "select") -or ($_ -eq "cascade")}{ Invoke-Expression('if ($f' + $Field.name + '.SelectedIndex -eq 0){$valid = $false; $f' + $Field.name + '.Backcolor = [System.Drawing.ColorTranslator]::FromHtml("' + $Settings.errorcolor + '")}else{$f' + $Field.name + '.ResetBackColor()}') } } } if(($valid -eq $true) -and ($cvalid -eq $true)){ Write-Verbose -Message "Get field values" ForEach ($Field in $Settings.fields){ Switch ($Field.type){ "text"{ $fval = Invoke-Expression('$f' + $Field.name + '.Text') If(!($Test)){SetTSVariable -Name $Field.name -Value $fval} } "password"{ $fval = Invoke-Expression('$f' + $Field.name + '.Text') | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString If(!($Test)){SetTSVariable -Name $Field.name -Value $fval} } "radio" { Foreach ($radiooption in $Field.options.psobject.properties){ Invoke-Expression('if($f' + $Field.name + '_' + $radiooption.name + '.checked -eq $true){$Fval = $f' + $Field.name + '_' + $radiooption.name + '}') } $flabel = $Field.options.psobject.Properties | Where-Object {$_.Value -eq $($fval.Text)} | Select-Object -ExpandProperty Name If(!($Test)){SetTSVariable -Name $Field.name -Value $fval.Text} If(!($Test)){SetTSVariable -Name "$($Field.Name)_label" -Value $flabel} } "check" { $fval = Invoke-Expression('$f' + $Field.name + '.Checked') If(!($Test)){SetTSVariable -Name $Field.name -Value $fval} } "select" { $fval = Invoke-Expression('$f' + $Field.name + '.SelectedItem') If(!($Test)){SetTSVariable -Name $Field.name -Value $fval.Value} If(!($Test)){SetTSVariable -Name "$($Field.Name)_label" -Value $fval.Label} } "cascade" { $fval = Invoke-Expression('$f' + $Field.name + '.SelectedItem') If(!($Test)){SetTSVariable -Name $Field.name -Value $fval.Value} If(!($Test)){SetTSVariable -Name "$($Field.Name)_label" -Value $fval.Label} } } } $objForm.Close() } } $objForm.Controls.Add($OKButton) $footerfield = New-Object System.Windows.Forms.Label $footerfield.Dock = [System.Windows.Forms.DockStyle]::Bottom $footerfield.Text = $Settings.footer $footerfield.Font = $LabelFont $footerfield.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter $footerfield.Height = 50 $footerfield.BackColor = [System.Drawing.ColorTranslator]::FromHtml($Settings.errorcolor) $objForm.Controls.Add($footerfield) Write-Verbose -Message "Show dialog" $objForm.Topmost = $True $objForm.Add_Shown({$objForm.Activate()}) [void]$objForm.ShowDialog() } End{ Write-Verbose -Message "Done" }
Document Actions