<# <# .SYNOPSIS This script provides a GUI administrative interface for Lync's Analog Devices & Common Area Phones. It takes no command-line parameters. .DESCRIPTION MAdCaP excuses the administrator from having to remember the syntax for the creation of Analog Devices and Common Area Phones. It also captures the installation's existing parameters (SIP domains, Gateway FQDNs, Dial Plans and Policies), enabling them to be easily selected. .NOTES Version : 1.9 Date : 29th September 2017 Lync Version : 2010, 2013 & SfB2015 Author : Greig Sheridan Header stolen from : Pat Richard's amazing "Get-CsConnections.ps1" WISH-LIST / TODO: - Filter existing item tab by pool / site. (Will also speed its load time) Revision History v1.9 - 29th September 2017 Fixed bug where selecting the "Existing Objects" tab on launch (without doing anything else) didn't pick up the default. v1.8 - 22nd September 2017 Added the OU picker to the "Existing Objects" tab Added a "Select & Make Default" button to the OU picker & associated code to save/read config Saved the chosen RegistrarPool & SIPDomain to the new config file Corrected minor whoops: the OK message after creating a new device referenced the "Existing Item" tab but it's correctly the "Existing Object" tab Revised OU TextBox & DN TextBox to remove the code that moves the cursor to the end of the line with every character Neatened OU TextBox & DN TextBox to remove duplicate calls to handler_ValidateGo v1.7 - 27th June 2017 MAJOR UPDATE / REFRESH: Added the "OU" picker stolen from Anthony Caragol's brilliant Lync_Common_Area_Phone_Tool_v1_2.ps1 (https://gallery.technet.microsoft.com/Lync-2013-Common-Area-57bc4ff1) Added the "sticky" enhancement so it opens to the previously selected OU. Tip: Copy/Paste from an Existing object on the other tab! Rearranged the tabs: Policies are now only visible for Existing objects (timing problems caused real headaches trying to set a Policy or PIN when you initially create the object) Disabled the PIN text box on the "Existing Object" tab if you've ONLY selected an Analog Device Enabled "DN" for AnalogDevices (previously not an option - was that old Lync 2010 behaviour or a coding error?) Script now lets you specify a DN and an OU - an invalid pairing - but will disregard/ignore the OU & use the DN Changed script to (re)populate the AD & CA phone lists each time you select the "Existing Item" tab - where it previously did this on script load When an object is created on the New tab and you select the Existing tab the script now pre-selects that object (if it exists when we query for it) - if it doesn't and you press Refresh, it will auto-select the object just created Added a popup MsgBox to indicate success/fail after creating an object on the New tab Added "-warningaction silentlycontinue" liberally to suppress the yellow that sprays in the underlying P$ window if you've been deleting policies, etc that are still assigned to users or devices v1.6 - 1st March 2016 Added Location Policy to the policies you can set. (Thank you @JohnACook) v1.5 - 28th Dec 2014 Signed the script with my code-signing certificate (thanks DigiCert!) Changed "$objNewDisplayNumberTextBox.add_TextChanged" to accept a dash as valid v1.4 - 1st Nov 2013 Added quotes around Gateway, Line URI & PIN before sending them to Lync v1.3 - 11th June 2013 Corrected bug where I was incorrectly sending "DN=" instead of "CN=" to create a CommonAreaPhone referencing an existing object v1.2 - 26th April 2013 Added quotes around registrar in "$objGoButton.Add_Click" as FQDNs with '-' were being rejected and raising errors. v1.1 - 5th March 2013 Revised "$objNewLineUriTextBox.add_TextChanged" to support ";ext=" v1.0 - 29th December 2012 Initial release .LINK https://greiginsydney.com/madcap-ps1-a-gui-for-lync-analog-devices-common-area-phones #> [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") Import-Module ActiveDirectory # For Windows 2008 Support $global:Icon = [system.drawing.icon]::ExtractAssociatedIcon($PSHOME + "\powershell.exe") $global:LastTouchedObject = "" $global:AddCAPselectedOU = "" $Global:DefaultOU = "" $Global:DefaultPool = "" $Global:DefaultSIPDomain = "" $scriptpath = $MyInvocation.MyCommand.Path $Configfile = ([System.IO.Path]::ChangeExtension($scriptpath, "xml")) # ============================================================================ # START FUNCTIONS ============================================================ # ============================================================================ Function Add-QuickOU-Node($Nodes, $Path, $DefaultSelectedOU) { $OUArray=$Path.Split(",") [array]::Reverse($OuArray) $SelectPath="" $OuArray | %{ if ($SelectPath.length -eq 0) {$SelectPath=$_} else {$SelectPath = $_ + "," + $SelectPath} $FindIt = $Nodes.Find($_, $False) if ($FindIt.Count -eq 1) { $Nodes = $FindIt[0].Nodes } else { $Node = New-Object Windows.Forms.TreeNode($_) $Node.Name = $_ $Node.Tag = $SelectPath [void]$Nodes.Add($Node) $FindIt = $Nodes.Find($_, $False) $Nodes = $FindIt[0].Nodes } if ($FindIt.tag -eq $DefaultSelectedOU) { $Global:NodeToShow = $FindIt[0] } } } Function Show-QuickOu-Form($DefaultSelectedOU) { $SelectOUForm = New-Object Windows.Forms.Form $SelectOUForm.Size = New-Object System.Drawing.Size(515,580) $SelectOUForm.StartPosition = "CenterScreen" # Manual, WindowsDefaultLocation, WindowsDefaultBounds, CenterParent $SelectOUForm.FormBorderStyle = "FixedDialog" # FixedSingle, Fixed3D, FixedDialog, FixedToolWindow $SelectOuForm.MaximizeBox = $false $SelectOuForm.Text = "Please Select an Organizational Unit" $SelectOuForm.Icon = $Global:Icon $OUTreeView = New-Object Windows.Forms.TreeView $OUTreeView.PathSeparator = "," $OUTreeView.Size = New-Object System.Drawing.Size(500,500) $OUTreeView.SelectAll $SelectOUForm.Controls.Add($OUTreeView) $objIPProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties() $strDNSDomain = $objIPProperties.DomainName.toLower() $strDomainDN = $strDNSDomain.toString().split('.'); foreach ($strVal in $strDomainDN) {$strTemp += "dc=$strVal,"}; $strDomainDN = $strTemp.TrimEnd(",").toLower() $AllOUs= Get-ADObject -Filter 'ObjectClass -eq "organizationalUnit"' -SearchScope SubTree -SearchBase $strDomainDN #$AllOUs= Get-ADObject -Filter 'ObjectClass -eq "person"' -SearchScope SubTree -SearchBase $strDomainDN ForEach ($OU in $AllOUs) { $MyOU=$OU.DistinguishedName Add-QuickOU-Node $OUTreeView.Nodes $MyOU $DefaultSelectedOU } $OUTreeView.SelectedNode = $Global:NodeToShow $SelectOUButton = New-Object System.Windows.Forms.Button $SelectOUButton.Location = New-Object System.Drawing.Size(10,510) $SelectOUButton.Size = New-Object System.Drawing.Size(150,25) $SelectOUButton.Text = "Select" $SelectOUButton.Add_Click({ $Global:AddCAPselectedOU = $OUTreeView.SelectedNode.tag $SelectOUForm.Close() }) $SelectOUButton.Anchor = 'Bottom, Left' $SelectOUForm.Controls.Add($SelectOUButton) $MakeDefaultButton = New-Object System.Windows.Forms.Button $MakeDefaultButton.Location = New-Object System.Drawing.Size(170,510) $MakeDefaultButton.Size = New-Object System.Drawing.Size(150,25) $MakeDefaultButton.Text = "Select && Make Default" $MakeDefaultButton.Add_Click({ $Global:AddCAPselectedOU = $OUTreeView.SelectedNode.tag $Global:DefaultOU = $OUTreeView.SelectedNode.tag $SelectOUForm.Close() }) $MakeDefaultButton.Anchor = 'Bottom, Left' $SelectOUForm.Controls.Add($MakeDefaultButton) $CancelOUButton = New-Object System.Windows.Forms.Button $CancelOUButton.Location = New-Object System.Drawing.Size(335,510) $CancelOUButton.Size = New-Object System.Drawing.Size(150,25) $CancelOUButton.Text = "Cancel" $CancelOUButton.Add_Click({ $SelectOUForm.Close()}) $CancelOUButton.Anchor = 'Bottom, Left' $SelectOUForm.Controls.Add($CancelOUButton) $SelectOUForm.ShowDialog() $SelectOUForm.Dispose() } Function ReadSettings () { if (Test-Path -Path "$($Configfile)") { try { $xml = [xml](get-Content -path "$($Configfile)") $myDefaultOU = $xml.configuration.DefaultOU $myDefaultPool = $xml.configuration.DefaultPool $myDefaultSIPDomain = $xml.configuration.DefaultSIPDomain } catch { $myDefaultOU = "" $myDefaultPool = "" $myDefaultSIPDomain = "" } } else { # No file? Apply some defaults: $myDefaultOU = "" $myDefaultPool = "" $myDefaultSIPDomain = "" } return $myDefaultOU,$myDefaultPool,$myDefaultSIPDomain } Function WriteSettings () { param ([string]$myConfigfile, [string]$myDefaultOU, [string]$myDefaultPool, [string]$myDefaultSIPDomain) $SavedOU,$SavedPool,$SavedSIPDomain = ReadSettings if (($SavedOU.CompareTo($myDefaultOU) -eq 0) ` -and ($SavedPool.CompareTo($myDefaultPool) -eq 0) ` -and ($SavedSIPDomain.CompareTo($myDefaultSIPDomain) -eq 0)) { #No need to touch the file, there are no changes } else { [xml]$Doc = New-Object System.Xml.XmlDocument $Dec = $Doc.CreateXmlDeclaration("1.0","UTF-8",$null) $Doc.AppendChild($Dec) | out-null $Root = $Doc.CreateNode("element","configuration",$null) $Element = $Doc.CreateElement("DefaultOU") $Element.InnerText = $myDefaultOU $Root.AppendChild($Element) | out-null $Element = $Doc.CreateElement("DefaultPool") $Element.InnerText = $myDefaultPool $Root.AppendChild($Element) | out-null $Element = $Doc.CreateElement("DefaultSIPDomain") $Element.InnerText = $myDefaultSIPDomain $Root.AppendChild($Element) | out-null $Doc.AppendChild($Root) | out-null try { $Doc.save(("$($myConfigfile)")) } catch { } } } # ============================================================================ # END FUNCTIONS ============================================================== # ============================================================================ $Global:DefaultOU,$Global:DefaultPool,$Global:DefaultSIPDomain = ReadSettings #This has to happen here otherwise the form won't populate with the defaults. $global:AddCAPselectedOU = $Global:DefaultOU # ============================================================================ # Define the form ============================================================ # ============================================================================ $objForm = New-Object System.Windows.Forms.Form $objForm.Text = "MAdCaP.ps1" $objForm.Size = New-Object System.Drawing.Size(780,680) $objForm.StartPosition = "CenterScreen" # Manual, WindowsDefaultLocation, WindowsDefaultBounds, CenterParent $objForm.Autosize = $False $objForm.FormBorderStyle = "FixedDialog" # FixedSingle, Fixed3D, FixedDialog, FixedToolWindow $objForm.MinimizeBox = $True $objForm.MaximizeBox = $False $objForm.WindowState = "Normal" # Maximized, Minimized, Normal $objForm.SizeGripStyle = "Hide" # Auto, Hide, Show $objForm.ShowInTaskbar = $True $objForm.Icon = $Global:Icon # ============================================================================ # Define the TAB structure =================================================== # ============================================================================ $objTabControl = New-Object System.Windows.Forms.TabControl $objTabControl.Location = New-Object System.Drawing.Point(15,15) $objTabControl.Name = "tabControl" $objTabControl.Size = New-Object System.Drawing.Size(735,540) $objForm.Controls.Add($objTabControl) $objTabPageNew = New-Object System.Windows.Forms.TabPage $objTabPageNew.Name = "tabPageNew" $objTabPageNew.Text = "New Object" $objTabControl.Controls.Add($objTabPageNew) $objTabPageSet = New-Object System.Windows.Forms.TabPage $objTabPageSet.Name = "tabPageSet" $objTabPageSet.Text = "Existing Object" $objTabControl.Controls.Add($objTabPageSet) $objTabControl.Add_Click({handler_ValidateGo}) $objTabControl.Add_SelectedIndexChanged( { if ($objTabControl.SelectedIndex -eq 1) { Update-DeviceList Update-Display } }) # ============================================================================ # Add some descriptive / instructional text ================================== # ============================================================================ $objExplanationTextBox = New-Object System.Windows.Forms.Label $objExplanationTextBox.Location = New-Object System.Drawing.Size(300,25) $objExplanationTextBox.Size = New-Object System.Drawing.Size(440,40) $objExplanationTextBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) #$objExplanationTextBox.ForeColor = [System.Drawing.Color]::FromArgb(255,176,196,222) $objExplanationTextBox.ForeColor = [System.Drawing.Color]::"DarkBlue" $objExplanationTextBox.Text = "Create new objects on this tab.`nSelect the ""Existing Object"" tab to set Policies and a PIN" $objTabPageNew.Controls.Add($objExplanationTextBox) # ============================================================================ # Add the Analog Device / Common Area Phone Radio buttons ==================== # ============================================================================ $objRadiobuttonAD = New-Object System.Windows.Forms.RadioButton $objRadiobuttonAD.Location = New-Object System.Drawing.Point(25,20) $objRadiobuttonAD.Name = "AnalogDevice" $objRadiobuttonAD.Size = New-Object System.Drawing.Size(180,20) $objRadiobuttonAD.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objRadiobuttonAD.TabStop = $True $objRadiobuttonAD.Checked = $True $objRadiobuttonAD.Text = "Analog Device" $objRadiobuttonAD.Add_CheckedChanged( { if ($objRadiobuttonAD.Checked -eq $True) { $objNewAnalogFaxTitleBox.Enabled = $True $objNewAnalogFaxComboBox.Enabled = $True $objNewGatewayTitleBox.Enabled = $True $objNewPstnGatewayCombobox.Enabled = $True $objNewDescriptionTextBox.Enabled = $False } else { $objNewAnalogFaxTitleBox.Enabled = $False $objNewAnalogFaxComboBox.Enabled = $False $objNewGatewayTitleBox.Enabled = $False $objNewPstnGatewayCombobox.Enabled = $False $objNewDescriptionTextBox.Enabled = $True } }) $objTabPageNew.Controls.Add($objRadiobuttonAD) $objRadiobuttonCA = New-Object System.Windows.Forms.RadioButton $objRadiobuttonCA.Location = New-Object System.Drawing.Point(25,45) $objRadiobuttonCA.Name = "CommonAreaPhone" $objRadiobuttonCA.Size = New-Object System.Drawing.Size(180,20) $objRadiobuttonCA.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objRadiobuttonCA.TabStop = $True $objRadiobuttonCA.Text = "Common Area Phone" $objTabPageNew.Controls.Add($objRadiobuttonCA) $objRadiobuttonCA.add_CheckedChanged({handler_ValidateGo}) # ============================================================================ # Add the "Required" group box =============================================== # ============================================================================ $objNewRequiredGroupBox = New-Object System.Windows.Forms.GroupBox $objNewRequiredGroupBox.Location = New-Object System.Drawing.Size(15,75) $objNewRequiredGroupBox.Size = New-Object System.Drawing.Size(695,250) $objNewRequiredGroupBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewRequiredGroupBox.Text = "Required Parameters" $objTabPageNew.Controls.Add($objNewRequiredGroupBox) # ============================================================================ # Line URI =================================================================== # ============================================================================ $objNewLineUriTitleBox = New-Object System.Windows.Forms.Label $objNewLineUriTitleBox.Location = New-Object System.Drawing.Size(15,32) $objNewLineUriTitleBox.Size = New-Object System.Drawing.Size(60,20) $objNewLineUriTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewLineUriTitleBox.Text = "Line URI" $objNewRequiredGroupBox.Controls.Add($objNewLineUriTitleBox) $objNewLineUriTelBox = New-Object System.Windows.Forms.Label $objNewLineUriTelBox.Location = New-Object System.Drawing.Size(107,32) $objNewLineUriTelBox.Size = New-Object System.Drawing.Size(28,20) $objNewLineUriTelBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objNewLineUriTelBox.Text = "tel:" $objNewRequiredGroupBox.Controls.Add($objNewLineUriTelBox) $objNewLineUriTextBox = New-Object System.Windows.Forms.TextBox $objNewLineUriTextBox.Location = New-Object System.Drawing.Size(135,30) $objNewLineUriTextBox.Size = New-Object System.Drawing.Size(270,20) $objNewLineUriTextBox.Multiline = $False $objNewLineUriTextBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewLineUriTextBox.ReadOnly = $False $objNewLineUriTextBox.Text = "" $objNewRequiredGroupBox.Controls.Add($objNewLineUriTextBox) # ============================================================================ # Validate Line URI text values ============================================== # ============================================================================ $objNewLineUriTextBox.add_TextChanged( { # Only accept digits 0-9 and "+" in this field $objNewLineUriTextBox.Text = [regex]::replace($objNewLineUriTextBox.Text, "([^0-9+;ext=])" , "") $objNewLineUriTextBox.SelectionStart = $objNewLineUriTextBox.Text.Length }) $objNewLineUriTextBox.add_TextChanged({handler_ValidateGo}) # ============================================================================ # Add the ComboBox containing the Registrar Pool ============================= # ============================================================================ $objNewRegistrarPoolTitleBox = New-Object System.Windows.Forms.Label $objNewRegistrarPoolTitleBox.Location = New-Object System.Drawing.Size(15,62) $objNewRegistrarPoolTitleBox.Size = New-Object System.Drawing.Size(105,20) $objNewRegistrarPoolTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewRegistrarPoolTitleBox.Text = "Registrar Pool" $objNewRequiredGroupBox.Controls.Add($objNewRegistrarPoolTitleBox) $objNewRegistrarPoolCombobox = New-Object System.Windows.Forms.Combobox $objNewRegistrarPoolCombobox.Location = New-Object System.Drawing.Size(135,60) $objNewRegistrarPoolCombobox.Size = New-Object System.Drawing.Size(270,20) $objNewRegistrarPoolCombobox.Font = New-Object System.Drawing.Font("Arial", "9") $objNewRegistrarPoolCombobox.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList $items = Invoke-Expression "get-csservice -registrar" foreach ($item in $items) { [void] $objNewRegistrarPoolCombobox.Items.Add($item.poolFQDN) } # If there's a default, select it: if ($Global:DefaultPool -ne "") { $objNewRegistrarPoolCombobox.SelectedIndex = $objNewRegistrarPoolCombobox.findstring("$Global:DefaultPool") } # If the above failed to match or there's no Default, select the first one in the list: if ($objNewRegistrarPoolCombobox.SelectedIndex -eq -1) { $objNewRegistrarPoolCombobox.SelectedIndex = 0 # Automatically select the first one } $objNewRegistrarPoolCombobox.Add_SelectedIndexChanged({ $Global:DefaultPool = $objNewRegistrarPoolCombobox.SelectedItem }) $objNewRequiredGroupBox.Controls.Add($objNewRegistrarPoolCombobox) # ============================================================================ # Add the text for the OU ==================================================== # ============================================================================ $objNewOuTitleBox = New-Object System.Windows.Forms.Label $objNewOuTitleBox.Location = New-Object System.Drawing.Size(15,92) $objNewOuTitleBox.Size = New-Object System.Drawing.Size(60,20) $objNewOuTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewOuTitleBox.Text = "OU" $objNewRequiredGroupBox.Controls.Add($objNewOuTitleBox) $objNewOuTextBox = New-Object System.Windows.Forms.TextBox $objNewOuTextBox.Location = New-Object System.Drawing.Size(135,90) $objNewOuTextBox.Size = New-Object System.Drawing.Size(436,20) $objNewOuTextBox.Multiline = $False $objNewOuTextBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewOuTextBox.ReadOnly = $False $objNewOuTextBox.Text = $Global:DefaultOU $objNewRequiredGroupBox.Controls.Add($objNewOuTextBox) $ToolTip = New-Object System.Windows.Forms.ToolTip $ToolTip.BackColor = [System.Drawing.Color]::LightGoldenrodYellow $ToolTip.IsBalloon = $true $ToolTip.InitialDelay = 500 $ToolTip.ReshowDelay = 500 $ToolTip.SetToolTip($objNewOuTextBox, "Enter in the format OU=blah,CN=contoso,CN=local") $ToolTip.SetToolTip($objNewOuTitleBox, "Enter in the format OU=blah,CN=contoso,CN=local") $objNewOuBrowseButton = New-Object System.Windows.Forms.Button $objNewOuBrowseButton.Name = "Browse" $objNewOuBrowseButton.Text = "Browse" $objNewOuBrowseButton.TabIndex = 7 $objNewOuBrowseButton.Size = New-Object System.Drawing.Size(60,20) $objNewOuBrowseButton.Location = New-Object System.Drawing.Size(586,90) $objNewOuBrowseButton.Add_Click({ if ($Global:AddCAPselectedOU -eq "") { $Global:AddCAPselectedOU = $Global:DefaultOU } Show-QuickOu-Form ($objNewOuTextBox.Text) $objNewOuTextBox.Text=$Global:AddCAPselectedOU }) $objNewRequiredGroupBox.Controls.Add($objNewOuBrowseButton) # ============================================================================ # Validate the OU ============================================================ # ============================================================================ $objNewOuTextBox.add_TextChanged( { # # Prevent them adding quotes around it $objNewOuTextBox.Text = [regex]::replace($objNewOuTextBox.Text, '"' , "") #$objNewOuTextBox.SelectionStart = $objNewOuTextBox.Text.Length $Global:AddCAPselectedOU = $objNewOuTextBox.Text $objNewOuTextBox.add_TextChanged({handler_ValidateGo}) }) # ============================================================================ # Add the text for the DN ==================================================== # ============================================================================ $objNewDnTitleBox = New-Object System.Windows.Forms.Label $objNewDnTitleBox.Location = New-Object System.Drawing.Size(15,122) $objNewDnTitleBox.Size = New-Object System.Drawing.Size(60,20) $objNewDnTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewDnTitleBox.Text = "DN" $objNewRequiredGroupBox.Controls.Add($objNewDnTitleBox) $objNewDnTextBox = New-Object System.Windows.Forms.TextBox $objNewDnTextBox.Location = New-Object System.Drawing.Size(135,120) $objNewDnTextBox.Size = New-Object System.Drawing.Size(436,20) $objNewDnTextBox.Multiline = $False $objNewDnTextBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewDnTextBox.ReadOnly = $False $objNewDnTextBox.Text = "" $objNewRequiredGroupBox.Controls.Add($objNewDnTextBox) #$objNewDnTextBox.add_TextChanged({handler_ValidateGo}) $ToolTip = New-Object System.Windows.Forms.ToolTip $ToolTip.BackColor = [System.Drawing.Color]::LightGoldenrodYellow $ToolTip.IsBalloon = $true $ToolTip.InitialDelay = 500 $ToolTip.ReshowDelay = 500 $ToolTip.SetToolTip($objNewDnTextBox, "Enter in the format CN=ExistingMeetingRoom,OU=blah,CN=contoso,CN=local") $ToolTip.SetToolTip($objNewDnTitleBox, "Enter in the format CN=ExistingMeetingRoom,OU=blah,CN=contoso,CN=local") $objNewDnWarningBox = New-Object System.Windows.Forms.Label $objNewDnWarningBox.Location = New-Object System.Drawing.Size(135,150) $objNewDnWarningBox.Size = New-Object System.Drawing.Size(340,20) $objNewDnWarningBox.Font = New-Object System.Drawing.Font("Arial", "9") $objNewDnWarningBox.Text = "(OU && DN are mutually exclusive. A DN overrides an OU)" $objNewRequiredGroupBox.Controls.Add($objNewDnWarningBox) # ============================================================================ # Validate the DN ============================================================ # ============================================================================ $objNewDnTextBox.add_TextChanged( { # Prevent them adding quotes around it $objNewDnTextBox.Text = [regex]::replace($objNewDnTextBox.Text, '"' , "") #$objNewDnTextBox.SelectionStart = $objNewDnTextBox.Text.Length if ($objNewDnTextBox.Text -ne "") { $objNewOuTextBox.Enabled = $False } else { $objNewOuTextBox.Enabled = $True } handler_ValidateGo }) # ============================================================================ # Add the Analog Fax ComboBox ================================================ # ============================================================================ $objNewAnalogFaxTitleBox = New-Object System.Windows.Forms.Label $objNewAnalogFaxTitleBox.Location = New-Object System.Drawing.Size(15,182) $objNewAnalogFaxTitleBox.Size = New-Object System.Drawing.Size(80,20) $objNewAnalogFaxTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewAnalogFaxTitleBox.Text = "Analog Fax" $objNewRequiredGroupBox.Controls.Add($objNewAnalogFaxTitleBox) $objNewAnalogFaxCombobox = New-Object System.Windows.Forms.Combobox $objNewAnalogFaxCombobox.Location = New-Object System.Drawing.Size(135,180) $objNewAnalogFaxCombobox.Size = New-Object System.Drawing.Size(270,20) $objNewAnalogFaxCombobox.Font = New-Object System.Drawing.Font("Arial", "9") $objNewAnalogFaxCombobox.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList [void] $objNewAnalogFaxCombobox.Items.Add("$True") [void] $objNewAnalogFaxCombobox.Items.Add("$False") $objNewAnalogFaxCombobox.SelectedIndex = 0 # Automatically select the first one $objNewRequiredGroupBox.Controls.Add($objNewAnalogFaxCombobox) # ============================================================================ # Add the text for the Gateway =============================================== # ============================================================================ $objNewGatewayTitleBox = New-Object System.Windows.Forms.Label $objNewGatewayTitleBox.Location = New-Object System.Drawing.Size(15,212) $objNewGatewayTitleBox.Size = New-Object System.Drawing.Size(80,20) $objNewGatewayTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewGatewayTitleBox.Text = "Gateway" $objNewRequiredGroupBox.Controls.Add($objNewGatewayTitleBox) $objNewPstnGatewayCombobox = New-Object System.Windows.Forms.Combobox $objNewPstnGatewayCombobox.Location = New-Object System.Drawing.Size(135,210) $objNewPstnGatewayCombobox.Size = New-Object System.Drawing.Size(270,20) $objNewPstnGatewayCombobox.Font = New-Object System.Drawing.Font("Arial", "9") $objNewPstnGatewayCombobox.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList $items = Invoke-Expression "Get-CsService -PstnGateway" foreach ($item in $items) { [void] $objNewPstnGatewayCombobox.Items.Add($item.poolFQDN) } $objNewPstnGatewayCombobox.SelectedIndex = 0 # Automatically select the first one $objNewRequiredGroupBox.Controls.Add($objNewPstnGatewayCombobox) # ============================================================================ # Add the "Optional" group box =============================================== # ============================================================================ $objNewOptionalGroupBox = New-Object System.Windows.Forms.GroupBox $objNewOptionalGroupBox.Location = New-Object System.Drawing.Size(15,335) $objNewOptionalGroupBox.Size = New-Object System.Drawing.Size(695,170) $objNewOptionalGroupBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewOptionalGroupBox.Text = "Optional Parameters" $objTabPageNew.Controls.Add($objNewOptionalGroupBox) # ============================================================================ # Add the Display Name ======================================================= # ============================================================================ $objNewDisplayNameTitleBox = New-Object System.Windows.Forms.Label $objNewDisplayNameTitleBox.Location = New-Object System.Drawing.Size(15,27) $objNewDisplayNameTitleBox.Size = New-Object System.Drawing.Size(90,20) $objNewDisplayNameTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewDisplayNameTitleBox.Text = "Display Name" $objNewOptionalGroupBox.Controls.Add($objNewDisplayNameTitleBox) $objNewDisplayNameTextBox = New-Object System.Windows.Forms.TextBox $objNewDisplayNameTextBox.Location = New-Object System.Drawing.Size(130,25) $objNewDisplayNameTextBox.Size = New-Object System.Drawing.Size(270,20) $objNewDisplayNameTextBox.Multiline = $False $objNewDisplayNameTextBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewDisplayNameTextBox.ReadOnly = $False $objNewDisplayNameTextBox.Text = "" $objNewOptionalGroupBox.Controls.Add($objNewDisplayNameTextBox) $objNewDisplayNameWarningBox = New-Object System.Windows.Forms.Label $objNewDisplayNameWarningBox.Location = New-Object System.Drawing.Size(15,50) $objNewDisplayNameWarningBox.Size = New-Object System.Drawing.Size(460,20) $objNewDisplayNameWarningBox.Font = New-Object System.Drawing.Font("Arial", "8") $objNewDisplayNameWarningBox.Text = "(Careful: If you nominate a DN, a new Display Name here will overwrite the existing name)" $objNewOptionalGroupBox.Controls.Add($objNewDisplayNameWarningBox) $ToolTip = New-Object System.Windows.Forms.ToolTip $ToolTip.BackColor = [System.Drawing.Color]::LightGoldenrodYellow $ToolTip.IsBalloon = $True $ToolTip.InitialDelay = 500 $ToolTip.ReshowDelay = 500 $ToolTip.SetToolTip($objNewDisplayNameTextBox, "Some punctuation characters disallowed. 64 character limit") $ToolTip.SetToolTip($objNewDisplayNameTitleBox, "Some punctuation characters disallowed. 64 character limit") # ============================================================================ # Validate the Display Name ================================================== # ============================================================================ $objNewDisplayNameTextBox.add_TextChanged( { # Reference: http://support.microsoft.com/kb/909264 - a period is acceptable, but not as the first character: if (($objNewDisplayNameTextBox.Text.Length -ge 1) -and ($objNewDisplayNameTextBox.Text.SubString(0,1) -eq ".")) { $objNewDisplayNameTextBox.Text = $objNewDisplayNameTextBox.Text.Remove(0,1) } # Reference: http://technet.microsoft.com/en-us/library/bb726984.aspx - block invalid characters: $objNewDisplayNameTextBox.Text = [regex]::replace($objNewDisplayNameTextBox.Text, '["/\\[\]:;|=,+*?<>]' , "") #Limit the input to 64 characters if ($objNewDisplayNameTextBox.Text.Length -gt 64) { $objNewDisplayNameTextBox.Text = $objNewDisplayNameTextBox.Text.SubString(0,64) } $objNewDisplayNameTextBox.SelectionStart = $objNewDisplayNameTextBox.Text.Length }) # ============================================================================ # Add the Label & Text box containing the SIP URI =========================== # ============================================================================ $objNewSipUriTitleBox = New-Object System.Windows.Forms.Label $objNewSipUriTitleBox.Location = New-Object System.Drawing.Size(15,77) $objNewSipUriTitleBox.Size = New-Object System.Drawing.Size(85,20) $objNewSipUriTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewSipUriTitleBox.Text = "SIP Address" $objNewOptionalGroupBox.Controls.Add($objNewSipUriTitleBox) $objNewSipUriSipBox = New-Object System.Windows.Forms.Label $objNewSipUriSipBox.Location = New-Object System.Drawing.Size(100,77) $objNewSipUriSipBox.Size = New-Object System.Drawing.Size(30,20) $objNewSipUriSipBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objNewSipUriSipBox.Text = "sip:" $objNewOptionalGroupBox.Controls.Add($objNewSipUriSipBox) $objNewSipUriTextBox = New-Object System.Windows.Forms.TextBox $objNewSipUriTextBox.Location = New-Object System.Drawing.Size(130,75) $objNewSipUriTextBox.Size = New-Object System.Drawing.Size(270,20) $objNewSipUriTextBox.Multiline = $False $objNewSipUriTextBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewSipUriTextBox.ReadOnly = $False $objNewSipUriTextBox.Text = "" $objNewOptionalGroupBox.Controls.Add($objNewSipUriTextBox) $objNewSipUriAtBox = New-Object System.Windows.Forms.Label $objNewSipUriAtBox.Location = New-Object System.Drawing.Size(402,77) $objNewSipUriAtBox.Size = New-Object System.Drawing.Size(16,20) $objNewSipUriAtBox.Font = New-Object System.Drawing.Font("Arial", "9") $objNewSipUriAtBox.Text = "@" $objNewOptionalGroupBox.Controls.Add($objNewSipUriAtBox) $objSipDomainCombobox = New-Object System.Windows.Forms.Combobox $objSipDomainCombobox.Location = New-Object System.Drawing.Size(420,75) $objSipDomainCombobox.Size = New-Object System.Drawing.Size(150,20) $objSipDomainCombobox.Font = New-Object System.Drawing.Font("Arial", "9") $objSipDomainCombobox.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList $items = Invoke-Expression "Get-CsSipDomain" foreach ($item in $items) { [void] $objSipDomainCombobox.Items.Add($item.Identity) } # If there's a default, select it: if ($Global:DefaultSIPDomain -ne "") { $objSipDomainCombobox.SelectedIndex = $objSipDomainCombobox.findstring("$Global:DefaultSIPDomain") } # If the above failed to match or there's no default, select the first one in the list: if ($objSipDomainCombobox.SelectedIndex -eq -1) { $objSipDomainCombobox.SelectedIndex = 0 # Automatically select the first one } $objSipDomainCombobox.Add_SelectedIndexChanged({$Global:DefaultSIPDomain = $objSipDomainCombobox.SelectedItem}) $objNewOptionalGroupBox.Controls.Add($objSipDomainCombobox) # ============================================================================ # Validate SIP text values =================================================== # ============================================================================ $objNewSipUriTextBox.add_TextChanged( { #Reference http://www.ietf.org/rfc/rfc3261.txt #Reference http://www.ietf.org/rfc/rfc2396.txt # + tests of what the Lync Control Panel will accept $objNewSipUriTextBox.Text = [regex]::replace($objNewSipUriTextBox.Text, "[^\w\.\-+;!~*()\047]" , "") # "\047" = single quote $objNewSipUriTextBox.SelectionStart = $objNewSipUriTextBox.Text.Length }) # ============================================================================ # Display Number ============================================================= # ============================================================================ $objNewDisplayNumberTitleBox = New-Object System.Windows.Forms.Label $objNewDisplayNumberTitleBox.Location = New-Object System.Drawing.Size(15,112) $objNewDisplayNumberTitleBox.Size = New-Object System.Drawing.Size(105,20) $objNewDisplayNumberTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewDisplayNumberTitleBox.Text = "Display Number" $objNewOptionalGroupBox.Controls.Add($objNewDisplayNumberTitleBox) $objNewDisplayNumberTextBox = New-Object System.Windows.Forms.TextBox $objNewDisplayNumberTextBox.Location = New-Object System.Drawing.Size(130,110) $objNewDisplayNumberTextBox.Size = New-Object System.Drawing.Size(270,20) $objNewDisplayNumberTextBox.Multiline = $False $objNewDisplayNumberTextBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewDisplayNumberTextBox.ReadOnly = $False $objNewDisplayNumberTextBox.Text = "" $objNewOptionalGroupBox.Controls.Add($objNewDisplayNumberTextBox) # ============================================================================ # Validate the Display Number ================================================ # ============================================================================ $objNewDisplayNumberTextBox.add_TextChanged( { # Only accept digits 0-9, "+", space, dash and round brackets in this field $objNewDisplayNumberTextBox.Text = [regex]::replace($objNewDisplayNumberTextBox.Text, "([^0-9+ ()-])" , "") $objNewDisplayNumberTextBox.SelectionStart = $objNewDisplayNumberTextBox.Text.Length }) # ============================================================================ # Description ================================================================ # ============================================================================ $objNewDescriptionTitleBox = New-Object System.Windows.Forms.Label $objNewDescriptionTitleBox.Location = New-Object System.Drawing.Size(15,142) $objNewDescriptionTitleBox.Size = New-Object System.Drawing.Size(105,20) $objNewDescriptionTitleBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewDescriptionTitleBox.Text = "Description" $objNewOptionalGroupBox.Controls.Add($objNewDescriptionTitleBox) $objNewDescriptionTextBox = New-Object System.Windows.Forms.TextBox $objNewDescriptionTextBox.Location = New-Object System.Drawing.Size(130,140) $objNewDescriptionTextBox.Size = New-Object System.Drawing.Size(436,20) $objNewDescriptionTextBox.Multiline = $False $objNewDescriptionTextBox.Font = New-Object System.Drawing.Font("Arial", "10") $objNewDescriptionTextBox.Enabled = $False # We default to Analog Device, so this box is disabled on launch $objNewDescriptionTextBox.Text = "" $objNewOptionalGroupBox.Controls.Add($objNewDescriptionTextBox) # ============================================================================ # Validate the Description =================================================== # ============================================================================ $objNewDescriptionTextBox.add_TextChanged( { #Stop the user adding any quotation marks. $objNewDescriptionTextBox.Text = [regex]::replace($objNewDescriptionTextBox.Text, '"' , "") #Limit the input to 1024 characters (unlikely??) if ($objNewDescriptionTextBox.Text.Length -gt 1024) { $objNewDescriptionTextBox.Text = $objNewDescriptionTextBox.Text.SubString(0,1024) } $objNewDescriptionTextBox.SelectionStart = $objNewDescriptionTextBox.Text.Length }) # ============================================================================ # Add the listbox containing the Get-CsAnalogDevices ========================= # ============================================================================ $objAnalogDeviceTitleBox = New-Object System.Windows.Forms.Label $objAnalogDeviceTitleBox.Location = New-Object System.Drawing.Size(15,15) $objAnalogDeviceTitleBox.Size = New-Object System.Drawing.Size(200,20) $objAnalogDeviceTitleBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objAnalogDeviceTitleBox.Text = "Analog Devices" $objTabPageSet.Controls.Add($objAnalogDeviceTitleBox) $objAnalogDeviceListbox = New-Object System.Windows.Forms.Listbox $objAnalogDeviceListbox.Location = New-Object System.Drawing.Size(15,40) $objAnalogDeviceListbox.Size = New-Object System.Drawing.Size(460,120) $objAnalogDeviceListbox.HorizontalScrollbar = $true $objAnalogDeviceListbox.SelectionMode = "MultiExtended" $objAnalogDeviceListbox.TabIndex = 1 $objTabPageSet.Controls.Add($objAnalogDeviceListbox) $objAnalogDeviceListbox.Add_Click({handler_ValidateGo}) $objAnalogDeviceListbox.Add_SelectedIndexChanged({Update-Display}) # ============================================================================ # Add the BROWSE button ===================================================== # ============================================================================ $objExistOuBrowseButton = New-Object System.Windows.Forms.Button $objExistOuBrowseButton.Name = "Browse" $objExistOuBrowseButton.Text = "Browse" $objExistOuBrowseButton.TabIndex = 7 $objExistOuBrowseButton.Size = New-Object System.Drawing.Size(90,25) $objExistOuBrowseButton.Location = New-Object System.Drawing.Size(280,10) $objExistOuBrowseButton.Add_Click({ if ($Global:AddCAPselectedOU -eq "") { $objNewOuTextBox.Text = $Global:DefaultOU } Show-QuickOu-Form ($objNewOuTextBox.Text) $objNewOuTextBox.Text=$Global:AddCAPselectedOU Update-DeviceList }) $objTabPageSet.Controls.Add($objExistOuBrowseButton) # ============================================================================ # Add the REFRESH button ===================================================== # ============================================================================ $objRefreshButton = New-Object System.Windows.Forms.Button $objRefreshButton.Name = "Refresh" $objRefreshButton.Text = "Refresh" $objRefreshButton.TabIndex = 7 $objRefreshButton.Size = "90,25" $objRefreshButton.Location = "385, 10" $objRefreshButton.Add_Click({Update-DeviceList}) $objTabPageSet.Controls.Add($objRefreshButton) # ============================================================================ # Add the listbox containing the Get-CsCommonAreaPhones ====================== # ============================================================================ $objCommonAreaPhoneTitleBox = New-Object System.Windows.Forms.Label $objCommonAreaPhoneTitleBox.Location = New-Object System.Drawing.Size(15,155) $objCommonAreaPhoneTitleBox.Size = New-Object System.Drawing.Size(200,20) $objCommonAreaPhoneTitleBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objCommonAreaPhoneTitleBox.Text = "Common Area Phones" $objTabPageSet.Controls.Add($objCommonAreaPhoneTitleBox) $objCommonAreaPhoneListbox = New-Object System.Windows.Forms.Listbox $objCommonAreaPhoneListbox.Location = New-Object System.Drawing.Size(15,180) $objCommonAreaPhoneListbox.Size = New-Object System.Drawing.Size(460,120) $objCommonAreaPhoneListbox.HorizontalScrollbar = $true $objCommonAreaPhoneListbox.SelectionMode = "MultiExtended" $objCommonAreaPhoneListbox.TabIndex = 2 $objTabPageSet.Controls.Add($objCommonAreaPhoneListbox) $objCommonAreaPhoneListbox.Add_Click({handler_ValidateGo}) $objCommonAreaPhoneListbox.Add_SelectedIndexChanged({Update-Display}) # ============================================================================ # Add the textbox displaying *1* item ======================================== # ============================================================================ $objOutputBox = New-Object System.Windows.Forms.TextBox $objOutputBox.Location = New-Object System.Drawing.Size(15,320) $objOutputBox.Size = New-Object System.Drawing.Size(460,160) $objOutputBox.Multiline = $True $objOutputBox.Font = New-Object System.Drawing.Font("Courier New", "10") $objOutputBox.Wordwrap = $True $objOutputBox.ReadOnly = $True $objOutputBox.TabStop = $False $objOutputBox.ScrollBars = [System.Windows.Forms.ScrollBars]::Both $objOutputBox.Text = "Select an Analog Device or Common Area Phone to display it" $objTabPageSet.Controls.Add($objOutputBox) function Update-Display { $AD_count = 0 $CA_count = 0 foreach ($AD in $objAnalogDeviceListbox.SelectedItems) { if ($AD -ne "") {$AD_count++} } foreach ($CA in $objCommonAreaPhoneListbox.SelectedItems) { if ($CA -ne "") {$CA_count++} } if (($AD_count -eq 1) -and ($CA_count -eq 0)) { $AD_name = $objAnalogDeviceListbox.SelectedItem if ($AD_name -ne "") { # Then they've not selected the first, empty value $objOutputBox.Text = Invoke-Expression ` "(get-csAnalogDevice ""$AD_name"" -warningaction silentlycontinue | Format-List | Out-String).Trim()" } } elseif (($AD_count -eq 0) -and ($CA_count -eq 1)) { $CA_name = $objCommonAreaPhoneListbox.SelectedItem if ($CA_name -ne "") { # Then they've not selected the first, empty value $objOutputBox.Text = Invoke-Expression ` "(get-csCommonAreaPhone ""$CA_name"" -warningaction silentlycontinue | Format-List | Out-String).Trim()" } } else { $objOutputBox.Text = "Select only 1 Analog Device or Common Area Phone to display it" } # write-host "AD Count = $($AD_count), CA Count = $($CA_Count)" } # ============================================================================ # Event Handler that updates the Analog Devices & Common Area Phones ========= # ============================================================================ function Update-DeviceList { # Flush both lists first: $objAnalogDeviceListbox.Items.Clear() $objCommonAreaPhoneListbox.Items.Clear() # Build the Analog Device list try { if ($Global:AddCAPselectedOU -ne "") { $items = Invoke-Expression "Get-CsAnalogDevice -OU ""$Global:AddCAPselectedOU"" -warningaction silentlycontinue" } else { $items = Invoke-Expression "Get-CsAnalogDevice -warningaction silentlycontinue" } } catch { # Most likely issue is no Analogs in the selected OU $items = @() } [void] $objAnalogDeviceListbox.Items.Add("") #Add the blank entry at the top $selectedIndex = 0 $ItemIndex = 1 foreach ($item in $items) { if ($item -ne $null) { if ($global:LastTouchedObject -ne "") { if ($global:LastTouchedObject -match $item.DistinguishedName) { $selectedIndex = $ItemIndex } } [void] $objAnalogDeviceListbox.Items.Add($item.Identity) $ItemIndex ++ } } $objAnalogDeviceListbox.SetSelected($selectedIndex,$true) $objAnalogDeviceListbox.TopIndex = ($objAnalogDeviceListbox.SelectedIndex) # Build the Common Area Phones list try { if ($Global:AddCAPselectedOU -ne "") { $items = Invoke-Expression "Get-CsCommonAreaPhone -OU ""$Global:AddCAPselectedOU"" -warningaction silentlycontinue" } else { $items = Invoke-Expression "Get-CsCommonAreaPhone -warningaction silentlycontinue" } } catch { # Most likely issue is no CAPs in the selected OU $items = @() } [void] $objCommonAreaPhoneListbox.Items.Add("") #Add the blank entry at the top $selectedIndex = 0 $ItemIndex = 1 foreach ($item in $items) { if ($item -ne $null) { if ($global:LastTouchedObject -ne "") { if ($global:LastTouchedObject -match $item.DistinguishedName) { $selectedIndex = $ItemIndex } } [void] $objCommonAreaPhoneListbox.Items.Add($item.Identity) $ItemIndex ++ } } $objCommonAreaPhoneListbox.SetSelected($selectedIndex,$true) $objCommonAreaPhoneListbox.TopIndex = ($objCommonAreaPhoneListbox.SelectedIndex) } # ============================================================================ # Add the listbox containing the Get-CsDialPlans ============================= # ============================================================================ $objDialPlanTitleBox = New-Object System.Windows.Forms.Label $objDialPlanTitleBox.Location = New-Object System.Drawing.Size(500,15) $objDialPlanTitleBox.Size = New-Object System.Drawing.Size(220,20) $objDialPlanTitleBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objDialPlanTitleBox.Text = "Dial Plans" $objTabPageSet.Controls.Add($objDialPlanTitleBox) $objDialPlanListbox = New-Object System.Windows.Forms.Listbox $objDialPlanListbox.Location = New-Object System.Drawing.Size(500,35) $objDialPlanListbox.Size = New-Object System.Drawing.Size(220,90) $objDialPlanListbox.TabIndex = 3 try { $items = Invoke-Expression "Get-CsDialPlan -warningaction silentlycontinue | Select-Object Identity" } catch { $items = "" log ("Error populating Dial Plans: $_") } [void] $objDialPlanListbox.Items.Add("") [void] $objDialPlanListbox.Items.Add("") foreach ($item in $items) { if ($item.Identity -eq "Global") { continue} # Discard all but the user-scoped values if ($item.Identity.Startswith("Site:")) { continue} # Discard all but the user-scoped values if ($item.Identity.Startswith("Service:")) { continue} # Discard all but the user-scoped values [void] $objDialPlanListbox.Items.Add($item.Identity) } $objDialPlanListbox.SetSelected(0,$true) $objTabPageSet.Controls.Add($objDialPlanListbox) # ============================================================================ # Add the listbox containing the Get-CsVoicePolicies ========================= # ============================================================================ $objVoicePoliciesTitleBox = New-Object System.Windows.Forms.Label $objVoicePoliciesTitleBox.Location = New-Object System.Drawing.Size(500,125) $objVoicePoliciesTitleBox.Size = New-Object System.Drawing.Size(220,20) $objVoicePoliciesTitleBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objVoicePoliciesTitleBox.Text = "Voice Policies" $objTabPageSet.Controls.Add($objVoicePoliciesTitleBox) $objVoicePolicyListbox = New-Object System.Windows.Forms.Listbox $objVoicePolicyListbox.Location = New-Object System.Drawing.Size(500,145) $objVoicePolicyListbox.Size = New-Object System.Drawing.Size(220,90) $objVoicePolicyListbox.TabIndex = 4 try { $items = Invoke-Expression "Get-CsVoicePolicy -warningaction silentlycontinue | Select-Object Identity" } catch { $items = "" log ("Error populating Voice Policies: $_") } [void] $objVoicePolicyListbox.Items.Add("") [void] $objVoicePolicyListbox.Items.Add("") foreach ($item in $items) { if ($item.Identity -eq "Global") { continue} # Discard all but the user-scoped values if ($item.Identity.Startswith("Site:")) { continue} # Discard all but the user-scoped values if ($item.Identity.Startswith("Service:")) { continue} # Discard all but the user-scoped values [void] $objVoicePolicyListbox.Items.Add($item.Identity) } $objVoicePolicyListbox.SetSelected(0,$true) $objTabPageSet.Controls.Add($objVoicePolicyListbox) # ============================================================================ # Add the listbox containing the Get-CsClientPolicies ======================== # ============================================================================ $objClientPoliciesTitleBox = New-Object System.Windows.Forms.Label $objClientPoliciesTitleBox.Location = New-Object System.Drawing.Size(500,235) $objClientPoliciesTitleBox.Size = New-Object System.Drawing.Size(220,20) $objClientPoliciesTitleBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objClientPoliciesTitleBox.Text = "Client Policies" $objTabPageSet.Controls.Add($objClientPoliciesTitleBox) $objClientPolicyListbox = New-Object System.Windows.Forms.Listbox $objClientPolicyListbox.Location = New-Object System.Drawing.Size(500,255) $objClientPolicyListbox.Size = New-Object System.Drawing.Size(220,90) $objClientPolicyListbox.TabIndex = 5 try { $items = Invoke-Expression "Get-CsClientPolicy -warningaction silentlycontinue | Select-Object Identity" } catch { $items = "" log ("Error populating Client Policies: $_") } [void] $objClientPolicyListbox.Items.Add("") [void] $objClientPolicyListbox.Items.Add("") foreach ($item in $items) { if ($item.Identity -eq "Global") { continue} # Discard all but the user-scoped values if ($item.Identity.Startswith("Site:")) { continue} # Discard all but the user-scoped values if ($item.Identity.Startswith("Service:")) { continue} # Discard all but the user-scoped values [void] $objClientPolicyListbox.Items.Add($item.Identity) } $objClientPolicyListbox.SetSelected(0,$true) $objTabPageSet.Controls.Add($objClientPolicyListbox) # ============================================================================ # Add the listbox containing the Get-CsLocationPolicies ======================== # ============================================================================ $objLocationPoliciesTitleBox = New-Object System.Windows.Forms.Label $objLocationPoliciesTitleBox.Location = New-Object System.Drawing.Size(500,345) $objLocationPoliciesTitleBox.Size = New-Object System.Drawing.Size(220,20) $objLocationPoliciesTitleBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objLocationPoliciesTitleBox.Text = "Location Policies" $objTabPageSet.Controls.Add($objLocationPoliciesTitleBox) $objLocationPolicyListbox = New-Object System.Windows.Forms.Listbox $objLocationPolicyListbox.Location = New-Object System.Drawing.Size(500,365) $objLocationPolicyListbox.Size = New-Object System.Drawing.Size(220,90) $objLocationPolicyListbox.TabIndex = 5 try { $items = Invoke-Expression "Get-CsLocationPolicy -warningaction silentlycontinue | Select-Object Identity" } catch { $items = "" log ("Error populating Client Policies: $_") } [void] $objLocationPolicyListbox.Items.Add("") [void] $objLocationPolicyListbox.Items.Add("") foreach ($item in $items) { if ($item.Identity -eq "Global") { continue} # Discard all but the user-scoped values if ($item.Identity.Startswith("Site:")) { continue} # Discard all but the user-scoped values if ($item.Identity.Startswith("Service:")) { continue} # Discard all but the user-scoped values [void] $objLocationPolicyListbox.Items.Add($item.Identity) } $objLocationPolicyListbox.SetSelected(0,$true) $objTabPageSet.Controls.Add($objLocationPolicyListbox) # ============================================================================ # Add the PIN text box ======================================================= # ============================================================================ $objPinTitleBox = New-Object System.Windows.Forms.Label $objPinTitleBox.Location = New-Object System.Drawing.Size(500,457) $objPinTitleBox.Size = New-Object System.Drawing.Size(65,20) $objPinTitleBox.Font = New-Object System.Drawing.Font("Arial", "10",[System.Drawing.FontStyle]::Bold) $objPinTitleBox.Text = "User PIN" $objPinTitleBox.Enabled = $False $objTabPageSet.Controls.Add($objPinTitleBox) $objPinTextBox = New-Object System.Windows.Forms.TextBox $objPinTextBox.Location = New-Object System.Drawing.Size(570,455) $objPinTextBox.Size = New-Object System.Drawing.Size(100,20) $objPinTextBox.Multiline = $False $objPinTextBox.Font = New-Object System.Drawing.Font("Arial", "10") $objPinTextBox.ReadOnly = $True $objPinTextBox.TabIndex = 6 $objTabPageSet.Controls.Add($objPinTextBox) # ============================================================================ # Validate PIN text values =================================================== # ============================================================================ $objPinTextBox.add_TextChanged( { # Only accept digits 0-9 in this field $objPinTextBox.Text = [regex]::replace($objPinTextBox.Text, "\D" , "") $objPinTextBox.SelectionStart = $objPinTextBox.Text.Length }) # ============================================================================ # Event Handler that unlocks the GO button =================================== # ============================================================================ function handler_ValidateGo { if ($objTabControl.SelectedIndex -eq 0) { # "New" $valid = 1 #We start with the assumption that the test will be true. # Any of those that fail will reset it to 0 & disable the Go Button if ($objNewLineUriTextBox.Text -eq "") {$valid = 0} if (($objNewOuTextBox.Text -eq "") -and ($objNewDnTextBox.Text -eq "")) {$valid = 0} #At least 1 must be populated! if (($objNewDnTextBox.Text -eq "") -and ($objNewOuTextBox.Text -ne "") -and ($objNewOuTextBox.Text -notlike "*OU=*")) {$valid = 0} #If OU, it must contain "OU=" if (($objNewDnTextBox.Text -ne "") -and ($objNewDnTextBox.Text -notlike "*CN=*")) {$valid = 0} #If DN, it must contain "CN=" if ($valid -eq 1) { $objGoButton.Enabled = $True } else { $objGoButton.Enabled = $False } $objPinTitleBox.Enabled = $False #Always disabled for New entities $objPinTextBox.ReadOnly = $True # " } else { # "Existing" $objGoButton.Enabled = $False foreach ($AD in $objAnalogDeviceListbox.SelectedItems) { if ($AD -ne "") { $objGoButton.Enabled = $True $objPinTitleBox.Enabled = $False #Always disabled for Analogs $objPinTextBox.ReadOnly = $True # " } } foreach ($CA in $objCommonAreaPhoneListbox.SelectedItems) { if ($CA -ne "") { $objGoButton.Enabled = $True $objPinTitleBox.Enabled = $True #Always enabled for CAPs $objPinTextBox.ReadOnly = $False # " } } } } # ============================================================================ # Add the 'Monitor Window' frame ============================================= # ============================================================================ $objStatusBox = New-Object System.Windows.Forms.RichTextBox $objStatusBox.Location = New-Object System.Drawing.Size(15,570) $objStatusBox.Size = New-Object System.Drawing.Size(500,65) $objStatusBox.Multiline = $True $objStatusBox.Font = New-Object System.Drawing.Font("Courier New", "9") #$objStatusBox.ForeColor = [System.Drawing.Color]::"Black" $objStatusBox.Wordwrap = $False $objStatusBox.ReadOnly = $True $objStatusBox.TabStop = $False #$objStatusBox.ScrollBars = [System.Windows.Forms.ScrollBars]::Both $objStatusBox.Text = "Monitor Window" $objStatusBox.Add_TextChanged( { # Scroll to the most recent entry when-ever text is added $objStatusBox.SelectionStart = $objStatusBox.Text.Length $objStatusBox.ScrollToCaret() } ) $objForm.Controls.Add($objStatusBox) # ============================================================================ # Create the LOGGING filename and Add the check-box =========================== # ============================================================================ $date = (get-date).ToString('yyyyMMMdd') #Thanks Kathy: http://blogs.msdn.com/b/kathykam/archive/2006/09/29/.net-format-string-102_3a00_-datetime-format-string.aspx $LogFile = "MAdCaP-" + $date + ".log" $Log = $True #The logging code will refer to this Boolean $objLogCheckbox = New-Object System.Windows.Forms.Checkbox $objLogCheckbox.Name = "Log" $objLogCheckbox.Text = "Log to file $LogFile" $objLogCheckbox.Checked = $True $objLogCheckbox.TabIndex = 7 $objLogCheckbox.Size = "290,30" $objLogCheckbox.Location = "540, 570" $objLogCheckbox.Add_CheckedChanged( { if ($objLogCheckbox.Checked -eq $True) { $Log = $True } else { $Log = $False } }) $objForm.Controls.Add($objLogCheckbox) # ============================================================================ # Function SEND ============================================================== # 1) Logs the string to be sent ============================================== # 2) Appends the error redirector " 2>&1" to it ============================== # 3) Sends it! =============================================================== # 4) Logs the response ======================================================= # 5) Returns the response in case the calling code wants it ================== # ============================================================================ function Send () { param ([String]$data) Log ("Command Executed = $data") $data += " 2>&1" # Append the handler that will capture errors try { $response = Invoke-Expression $data #"$Response" will contain the object's name or blank if we succeeded, otherwise error text. } catch { $response = "Error caught by handler: $_" } Log ("Result Received = $response") return [string]$response } # ============================================================================ # Function LOG =============================================================== # Writes to the status display, and to the log file if enabled =============== # ============================================================================ function Log() { param ([String]$data) $CRLF = [System.Environment]::NewLine $objStatusBox.Text += $CRLF + $Data if ($Log -eq $True) { try { $Time = (get-date).ToString("HH:mm:ss") # Use "hh:mm:ss tt" if you want the time in 12-hour format: "01:09:42 PM" Write "$Time $data" | Out-File $Logfile -Append } catch { #Log the failure to screen if we're unable to write to the log file $objStatusBox.Text += $CRLF + "MAdCaP ERROR: Unable to write to log file" } } } # ============================================================================ # Add the GO button ========================================================== # ============================================================================ $objGoButton = New-Object System.Windows.Forms.Button $objGoButton.Name = "GO" $objGoButton.Text = "GO!" $objGoButton.TabIndex = 8 $objGoButton.Size = "90,35" $objGoButton.Location = "540, 600" $objGoButton.Enabled = $False $objGoButton.Add_Click( { if ($objTabControl.SelectedIndex -eq 0) { # We're creating a NEW Object if ($objRadiobuttonAD.Checked -eq $True) { # We're creating an Analog Device $transmit = "New-CsAnalogDevice " #Analog Fax $text = $objNewAnalogFaxComboBox.SelectedItem if ($text -eq "True") { $transmit += "-AnalogFax:`$True " } else { $transmit += "-AnalogFax:`$False " } #Gateway $text = $objNewPstnGatewayCombobox.SelectedItem $transmit += "-Gateway ""$text"" " } else { # It's a Common Area Phone $transmit = "New-CsCommonAreaPhone " #Description $text = $objNewDescriptionTextBox.Text if ($text -ne "") { $transmit += "-Description ""$text"" " } } #If DN is populated, use it, otherwise use OU $text = $objNewDnTextBox.Text if ($text -ne "") { $transmit += "-DN ""$text"" " } else { $text = $objNewOuTextBox.Text $transmit += "-OU ""$text"" " } #LineURI $text = $objNewLineUriTextBox.Text $transmit += "-LineUri ""tel:$text"" " #Registrar Pool $text = $objNewRegistrarPoolCombobox.SelectedItem $transmit += "-RegistrarPool ""$text"" " #Add the Optional parameters if they exist: #Sip Address $text = $objNewSipUriTextBox.Text if ($text -ne "") { $domain = $objSipDomainCombobox.SelectedItem $transmit += "-SipAddress ""sip:$text@$domain"" " } #DisplayName $text = $objNewDisplayNameTextBox.Text if ($text -ne "") { $transmit += "-DisplayName ""$text"" " } #DisplayNumber $text = $objNewDisplayNumberTextBox.Text if ($text -ne "") { $transmit += "-DisplayNumber ""$text"" " } $transmit.trim() # Remove the space from the end $result = send ($transmit) #"$result" will be the new object's identity if we created it OK, otherwise it'll contain an error message. if (($result -notlike '*Error*') -and ($result -notlike '*The object * already exists*')) { if ($result -like "CN=*") { $global:LastTouchedObject = $result $OKMessage = "New object created OK!`n`nClick Refresh on the ""Existing Object"" tab until the new object shows (it will be automatically selected for you) then set the required Policies and/or PIN." $discard = [windows.forms.messagebox]::show($OKMessage,'MAdCaP.ps1','Ok') } else { $global:LastTouchedObject = "" } } else { $global:LastTouchedObject = "" $discard = [windows.forms.messagebox]::show($result,'MAdCaP.ps1','Ok') } } else { # "Existing Object" # Do the analogs first: if ($objAnalogDeviceListbox.SelectedItems –ne $Null) { foreach ($AnalogDevice in $objAnalogDeviceListbox.SelectedItems) { Grant-Policy ($AnalogDevice) # Analog phones don't have a client PIN } } # Now the Common Area Phones: if ($objCommonAreaPhoneListbox.SelectedItems –ne $Null) { foreach ($CommonAreaPhone in $objCommonAreaPhoneListbox.SelectedItems) { Grant-Policy ($CommonAreaPhone) if ($objPinTextBox.Text -ne "") { $PIN = $objPinTextBox.Text $transmit = "Set-CsClientPin ""$CommonAreaPhone"" -PIN ""$PIN""" $discard = send ($transmit) # We don't use the value returned here, hence "$discard" } } } } }) $objForm.Controls.Add($objGoButton) # ============================================================================ # Function Grant-Policy ======================================================= # ============================================================================ function Grant-Policy() { param ([String]$device) if ($device -eq "") { #They've selected the "null" device. Abort continue } foreach ($DialPlan in $objDialPlanListbox.SelectedItem) { if ($DialPlan -eq "") {continue} #do nothing if the blank value was selected if ($DialPlan -eq "") {$DialPlan = "`$Null"} $transmit = "Grant-CsDialplan ""$device"" -PolicyName ""$DialPlan""" $discard = send ($transmit) # We don't use the value returned here, hence "$discard" } foreach ($VoicePolicy in $objVoicePolicyListbox.SelectedItem) { if ($VoicePolicy -eq "") {continue} #do nothing if the blank value was selected if ($VoicePolicy -eq "") {$VoicePolicy = "`$Null"} $transmit = "Grant-CsVoicepolicy ""$device"" -PolicyName ""$VoicePolicy""" $discard = send ($transmit) # We don't use the value returned here, hence "$discard" } foreach ($ClientPolicy in $objClientPolicyListbox.SelectedItem) { if ($ClientPolicy -eq "") {continue} #do nothing if the blank value was selected if ($ClientPolicy -eq "") {$ClientPolicy = "`$Null"} $transmit = "Grant-CsClientpolicy ""$device"" -PolicyName ""$ClientPolicy""" $discard = send ($transmit) # We don't use the value returned here, hence "$discard" } foreach ($LocationPolicy in $objLocationPolicyListbox.SelectedItem) { if ($LocationPolicy -eq "") {continue} #do nothing if the blank value was selected if ($LocationPolicy -eq "") {$LocationPolicy = "`$Null"} $transmit = "Grant-CsLocationPolicy ""$device"" -PolicyName ""$LocationPolicy""" $discard = send ($transmit) # We don't use the value returned here, hence "$discard" } } # ============================================================================ # Add the CANCEL button ====================================================== # ============================================================================ $objCancelButton = New-Object System.Windows.Forms.Button $objCancelButton.Name = "Cancel" $objCancelButton.Text = "Cancel" $objCancelButton.TabIndex = 9 $objCancelButton.Size = "90,35" $objCancelButton.Location = "650, 600" $objCancelButton.Add_Click( { if ($Log -eq $True) { Log ("============================= Clean exit =======================") } $objForm.Close() } ) $objForm.Controls.Add($objCancelButton) # =========================================================================== # Activate the form ========================================================= # =========================================================================== $objForm.Add_Shown({$objForm.Activate()}) if ($Log -eq $True) { Log ("============================== Launched ========================") } [void] $objForm.ShowDialog() WriteSettings $Configfile $Global:DefaultOU $Global:DefaultPool $Global:DefaultSIPDomain # END #References: # Corky Caragol's OU picker: https://gallery.technet.microsoft.com/Lync-2013-Common-Area-57bc4ff1 #Code signing certificate kindly provided by Digicert: # SIG # Begin signature block # MIIcfgYJKoZIhvcNAQcCoIIcbzCCHGsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUKqUZYG6Q0KfTr2dwcJF4ffFq # dHWgghetMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0B # AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk # IElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg # Q29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA # +NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ # 1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0 # sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6s # cKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4Tz # rGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg # 0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIB # ADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUH # AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI # KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0 # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaG # NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD # QS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0 # dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYE # FFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en # IZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06 # GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5j # DhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgC # PC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIy # sjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4Gb # T8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIFNjCC # BB6gAwIBAgIQAwz3lWBnLYLrwtPPzpxvljANBgkqhkiG9w0BAQsFADByMQswCQYD # VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln # aWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29k # ZSBTaWduaW5nIENBMB4XDTE2MTEyMDAwMDAwMFoXDTE4MDMyODEyMDAwMFowczEL # MAkGA1UEBhMCQVUxGDAWBgNVBAgTD05ldyBTb3V0aCBXYWxlczEYMBYGA1UEBxMP # UGV0ZXJzaGFtIE5vcnRoMRcwFQYDVQQKEw5HcmVpZyBTaGVyaWRhbjEXMBUGA1UE # AxMOR3JlaWcgU2hlcmlkYW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQCse7j22GMLDkV5l+XBO/AY3ZyNQBXstf+QBF8+3U704MV4DQTtzmQhdQucD59u # D9pcr4hAf5hcKjNd72XEbigQXQcuCzHt3MJYBH4bOjSVwZ89k2bifBUF9CnkMITd # DfEK80zrIBi/X/SGZiRH+QvruDlL1554pmYI2mTf45pYd9AJSAvHIYVNP9nv1qFN # qAAlPCSdXAUCTcJs/ViWI2zt/IKImrbNNl2FFcaLuii2lTjnUAZhPF5WeTMheiVH # yCr47mfDHOvzwOhMiuijl1tmwbUG52Im9Nb6DmB0xekpxkUaTDgrm8wjZ3E+WZgG # lVwihuXjlpSOnDvhGKqyn+x3AgMBAAGjggHFMIIBwTAfBgNVHSMEGDAWgBRaxLl7 # KgqjpepxA8Bg+S32ZXUOWDAdBgNVHQ4EFgQUiOhbX88AxsVFI1pk0PWY02wbdRgw # DgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGA1UdHwRwMG4w # NaAzoDGGL2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3Mt # ZzEuY3JsMDWgM6Axhi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1 # cmVkLWNzLWcxLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwDATAqMCgGCCsGAQUF # BwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAEEATCBhAYI # KwYBBQUHAQEEeDB2MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j # b20wTgYIKwYBBQUHMAKGQmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydFNIQTJBc3N1cmVkSURDb2RlU2lnbmluZ0NBLmNydDAMBgNVHRMBAf8EAjAA # MA0GCSqGSIb3DQEBCwUAA4IBAQBJQE0f85v98kVLNB+iHQmfaFcpCYeU5VwBjMG1 # T6ACzyf7NIEBF+QCvFIN8AjjhrI2Ubtq2ivESmhhWmRb0NrAHostCEGJmlc0MBCy # q4kcaPA8HZysLbpbqvjaC0knAJRgyXR0OAcCnJAPSxBLo22mjJZat2r6i6OzGah5 # lySGUy6bUBh5CNFsxwLdMoRu2Dl7ImU3OORSOjeu0pOHeqJSV74bPqtoXcPuaDpx # SkBm92PU7mD4DJqHkgB+7fXA3sOV2TydYPi2t5whT7dwDpjHWO5rA5FlCHC529le # Oox04Dlkg4zMJMBSgbAHWoY12dJN2tqdV35XEhUlpKPw11+gMIIGajCCBVKgAwIB # AgIQAwGaAjr/WLFr1tXq5hfwZjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJV # UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu # Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBBc3N1cmVkIElEIENBLTEwHhcNMTQxMDIy # MDAwMDAwWhcNMjQxMDIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzERMA8GA1UEChMI # RGlnaUNlcnQxJTAjBgNVBAMTHERpZ2lDZXJ0IFRpbWVzdGFtcCBSZXNwb25kZXIw # ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjZF38fLPggjXg4PbGKuZJ # dTvMbuBTqZ8fZFnmfGt/a4ydVfiS457VWmNbAklQ2YPOb2bu3cuF6V+l+dSHdIhE # OxnJ5fWRn8YUOawk6qhLLJGJzF4o9GS2ULf1ErNzlgpno75hn67z/RJ4dQ6mWxT9 # RSOOhkRVfRiGBYxVh3lIRvfKDo2n3k5f4qi2LVkCYYhhchhoubh87ubnNC8xd4Ew # H7s2AY3vJ+P3mvBMMWSN4+v6GYeofs/sjAw2W3rBerh4x8kGLkYQyI3oBGDbvHN0 # +k7Y/qpA8bLOcEaD6dpAoVk62RUJV5lWMJPzyWHM0AjMa+xiQpGsAsDvpPCJEY93 # AgMBAAGjggM1MIIDMTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNV # HSUBAf8EDDAKBggrBgEFBQcDCDCCAb8GA1UdIASCAbYwggGyMIIBoQYJYIZIAYb9 # bAcBMIIBkjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ # UzCCAWQGCCsGAQUFBwICMIIBVh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAgAHQA # aABpAHMAIABDAGUAcgB0AGkAZgBpAGMAYQB0AGUAIABjAG8AbgBzAHQAaQB0AHUA # dABlAHMAIABhAGMAYwBlAHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUAIABEAGkA # ZwBpAEMAZQByAHQAIABDAFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABSAGUA # bAB5AGkAbgBnACAAUABhAHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3AGgA # aQBjAGgAIABsAGkAbQBpAHQAIABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBkACAA # YQByAGUAIABpAG4AYwBvAHIAcABvAHIAYQB0AGUAZAAgAGgAZQByAGUAaQBuACAA # YgB5ACAAcgBlAGYAZQByAGUAbgBjAGUALjALBglghkgBhv1sAxUwHwYDVR0jBBgw # FoAUFQASKxOYspkH7R7for5XDStnAs0wHQYDVR0OBBYEFGFaTSS2STKdSip5GoNL # 9B6Jwcp9MH0GA1UdHwR2MHQwOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMDigNqA0hjJodHRwOi8vY3JsNC5k # aWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNybDB3BggrBgEFBQcB # AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr # BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNz # dXJlZElEQ0EtMS5jcnQwDQYJKoZIhvcNAQEFBQADggEBAJ0lfhszTbImgVybhs4j # IA+Ah+WI//+x1GosMe06FxlxF82pG7xaFjkAneNshORaQPveBgGMN/qbsZ0kfv4g # pFetW7easGAm6mlXIV00Lx9xsIOUGQVrNZAQoHuXx/Y/5+IRQaa9YtnwJz04HShv # OlIJ8OxwYtNiS7Dgc6aSwNOOMdgv420XEwbu5AO2FKvzj0OncZ0h3RTKFV2SQdr5 # D4HRmXQNJsQOfxu19aDxxncGKBXp2JPlVRbwuwqrHNtcSCdmyKOLChzlldquxC5Z # oGHd2vNtomHpigtt7BIYvfdVVEADkitrwlHCCkivsNRu4PQUCjob4489yq9qjXvc # 2EQwggbNMIIFtaADAgECAhAG/fkDlgOt6gAK6z8nu7obMA0GCSqGSIb3DQEBBQUA # MGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT # EHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQg # Um9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0yMTExMTAwMDAwMDBaMGIxCzAJBgNV # BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp # Y2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOiCLZn5ysJClaWAc0Bw0p5WVFyp # xNJBBo/JM/xNRZFcgZ/tLJz4FlnfnrUkFcKYubR3SdyJxArar8tea+2tsHEx6886 # QAxGTZPsi3o2CAOrDDT+GEmC/sfHMUiAfB6iD5IOUMnGh+s2P9gww/+m9/uizW9z # I/6sVgWQ8DIhFonGcIj5BZd9o8dD3QLoOz3tsUGj7T++25VIxO4es/K8DCuZ0MZd # EkKB4YNugnM/JksUkK5ZZgrEjb7SzgaurYRvSISbT0C58Uzyr5j79s5AXVz2qPEv # r+yJIvJrGGWxwXOt1/HYzx4KdFxCuGh+t9V3CidWfA9ipD8yFGCV/QcEogkCAwEA # AaOCA3owggN2MA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAyBggrBgEFBQcDAQYI # KwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwggHSBgNVHSAE # ggHJMIIBxTCCAbQGCmCGSAGG/WwAAQQwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8v # d3d3LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYB # BQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMA # ZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEA # YwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIA # dAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcA # IABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwA # aQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkA # bgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUA # ZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTASBgNVHRMBAf8ECDAGAQH/AgEA # MHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl # cnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v # RGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRo # dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0Eu # Y3JsMDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1 # cmVkSURSb290Q0EuY3JsMB0GA1UdDgQWBBQVABIrE5iymQftHt+ivlcNK2cCzTAf # BgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOC # AQEARlA+ybcoJKc4HbZbKa9Sz1LpMUerVlx71Q0LQbPv7HUfdDjyslxhopyVw1Dk # grkj0bo6hnKtOHisdV0XFzRyR4WUVtHruzaEd8wkpfMEGVWp5+Pnq2LN+4stkMLA # 0rWUvV5PsQXSDj0aqRRbpoYxYqioM+SbOafE9c4deHaUJXPkKqvPnHZL7V/CSxbk # S3BMAIke/MV5vEwSV/5f4R68Al2o/vsHOE8Nxl2RuQ9nRc3Wg+3nkg2NsWmMT/tZ # 4CMP0qquAHzunEIOz5HXJ7cW7g/DvXwKoO4sCFWFIrjrGBpN/CohrUkxg0eVd3Hc # sRtLSxwQnHcUwZ1PL1qVCCkQJjGCBDswggQ3AgEBMIGGMHIxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25p # bmcgQ0ECEAMM95VgZy2C68LTz86cb5YwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcC # AQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYB # BAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFLKIYpYVNRTx # tvzhZ5eCrnVKoboYMA0GCSqGSIb3DQEBAQUABIIBAAlnLyt2es2MXnDC3kenr3HH # 76njE1fdcJp397UAJer1Uk2/RNUgX0hZ+o4I8N78yAM3TAEGb87CUuexlzM7eRcb # e0XIf47j2RvSGhqXLp/Yz9DCfvsS1BYOBGJONQVdKVTg4CPcnLN6+/BmwTsBDKjx # 1I8JbAkFP8mIDNcbCuVHpOYeppmIheBImCuIDLnNIDin6KWNxt4L4mHxI998Bv2N # /ohHpLLoS+XDbGoMyqTot6dN5mRO6cTvn3n803YneGF2h50Xp7Z10U+Z585vPp5+ # 4NgAPt8pJ6vgsGMDrrQJFd2fgzwWCeHvS0OE8zkzeQbTYxlOo3PyS/fgrwCRPdeh # ggIPMIICCwYJKoZIhvcNAQkGMYIB/DCCAfgCAQEwdjBiMQswCQYDVQQGEwJVUzEV # MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t # MSEwHwYDVQQDExhEaWdpQ2VydCBBc3N1cmVkIElEIENBLTECEAMBmgI6/1ixa9bV # 6uYX8GYwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJ # KoZIhvcNAQkFMQ8XDTE3MDkyODIxMTIyMFowIwYJKoZIhvcNAQkEMRYEFDhZqQuA # 1w2xzz+UJSbMPRZj+ICtMA0GCSqGSIb3DQEBAQUABIIBAJ2v1C5H+7iBi4xncE3b # iFlItfkQ3/yyJ7xxqEkC4OL6pqQKhQ55XtXQib/UtYN2mrjo5ztHS4NnkpNsIqdj # 3+Gw+EaM2yAO+fUw02va4h6yCEBy3xjxVfXei9/KGi9RnK9ee3XAIlhEvPvEAjoY # v0n0wlALu3HebCLBg8CsVN4oH/I40RO20MdAr9J8MIwPXDQqn0Sm6v7U44pcRYt6 # MRV7C65vpmSGBWiQFaPvABHVftRAZsBrY6mFenggnwH8QWWzxORhJp6rYcCrF2QE # 4kCpjpGJFMJpIvFikvVbEm3mbv8P84isuKdZj/E+eKyNDVW/8w8ka0yteSJ8G1/Y # CWM= # SIG # End signature block