Powershell GUI buttons arranged perpendicularly - azure

I wrote a PS function to be run with ISE launch, so I can call it and switch the Azure subscription with a click of a button, but the buttons in GUI are listed horizontally, how can I change it to be shown perpendicularly?
Here's my code:
using namespace System.Management.Automation.Host
$sub = get-azsubscription | where-object -property State -eq 'Enabled'
function Set-Sub {
$Title = "Powershell ISE"
$Question = "Select default subscription"
$0 = [ChoiceDescription]::new($sub.Name[0], $sub.Name[0])
$1 = [ChoiceDescription]::new($sub.Name[1], $sub.Name[1])
$2 = [ChoiceDescription]::new($sub.Name[2], $sub.Name[2])
$3 = [ChoiceDescription]::new($sub.Name[3], $sub.Name[3])
$4 = [ChoiceDescription]::new($sub.Name[4], $sub.Name[4])
$options = [ChoiceDescription[]]($0, $1, $2, $3, $4)
$result = $host.ui.PromptForChoice($Title, $Question, $options, 0)
switch ($result) {
0 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[0])
select-azsubscription -subscriptionname $sub.Name[0]
}
1 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[1])
select-azsubscription -subscriptionname $sub.Name[1]
}
2 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[2])
select-azsubscription -subscriptionname $sub.Name[2]
}
3 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[3])
select-azsubscription -subscriptionname $sub.Name[3]
}
4 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[4])
select-azsubscription -subscriptionname $sub.Name[4]
}
}
}
$context = Get-AzContext
Write-Host -BackgroundColor Red -ForegroundColor White ("Currently used subscription is: "+$context.Name)
Write-Host -ForegroundColor White (" ")
and that's how GUI looks like:

If you are looking for a way to make it vertical choices, it is not possible at this moment, though you can do differently for prompting every single option so that end user can select it: code would look like below:
$t = "Location"
$msg = "What location?"
$CFT = New-Object System.Management.Automation.Host.ChoiceDescription "&A choice 1", "1"
[Environment]::NewLine
$CON = New-Object System.Management.Automation.Host.ChoiceDescription "&B choice 2", "2"
$ELP = New-Object System.Management.Automation.Host.ChoiceDescription "&C choice 3", "3"
$CFT1 = New-Object System.Management.Automation.Host.ChoiceDescription "&A1 choice 1", "4"
$CON1 = New-Object System.Management.Automation.Host.ChoiceDescription "&B1 choice 2", "5"
$ELP1 = New-Object System.Management.Automation.Host.ChoiceDescription "&C1 choice 3", "6"
[int]$defaultchoice = 2
$options = [System.Management.Automation.Host.ChoiceDescription[]]($CFT, $CON, $ELP,$CFT1, $CON1, $ELP1)
$result = $host.ui.PromptForChoice($t, $msg, $options[0], 0)
$result = $host.ui.PromptForChoice($t, $msg, $options[1], 0)
Main problem with this approach as it will present one menu at a time with different prompt box.
But for nicer PromptForChoice for the PowerShell Console Host , you can use it like below:
An alternative to the built-in PromptForChoice providing a consistent UI across different hosts
function Get-Choice {
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,Position=0)]
$Title,
[Parameter(Mandatory=$true,Position=1)]
[String[]]
$Options,
[Parameter(Position=2)]
$DefaultChoice = -1
)
if ($DefaultChoice -ne -1 -and ($DefaultChoice -gt $Options.Count -or $DefaultChoice -lt 1)){
Write-Warning "DefaultChoice needs to be a value between 1 and $($Options.Count) or -1 (for none)"
exit
}
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()
$script:result = ""
$form = New-Object System.Windows.Forms.Form
$form.FormBorderStyle = [Windows.Forms.FormBorderStyle]::FixedDialog
$form.BackColor = [Drawing.Color]::White
$form.TopMost = $True
$form.Text = $Title
$form.ControlBox = $False
$form.StartPosition = [Windows.Forms.FormStartPosition]::CenterScreen
#calculate width required based on longest option text and form title
$minFormWidth = 100
$formHeight = 44
$minButtonWidth = 70
$buttonHeight = 23
$buttonY = 12
$spacing = 10
$buttonWidth = [Windows.Forms.TextRenderer]::MeasureText((($Options | sort Length)[-1]),$form.Font).Width + 1
$buttonWidth = [Math]::Max($minButtonWidth, $buttonWidth)
$formWidth = [Windows.Forms.TextRenderer]::MeasureText($Title,$form.Font).Width
$spaceWidth = ($options.Count+1) * $spacing
$formWidth = ($formWidth, $minFormWidth, ($buttonWidth * $Options.Count + $spaceWidth) | Measure-Object -Maximum).Maximum
$form.ClientSize = New-Object System.Drawing.Size($formWidth,$formHeight)
$index = 0
#create the buttons dynamically based on the options
foreach ($option in $Options){
Set-Variable "button$index" -Value (New-Object System.Windows.Forms.Button)
$temp = Get-Variable "button$index" -ValueOnly
$temp.Size = New-Object System.Drawing.Size($buttonWidth,$buttonHeight)
$temp.UseVisualStyleBackColor = $True
$temp.Text = $option
$buttonX = ($index + 1) * $spacing + $index * $buttonWidth
$temp.Add_Click({
$script:result = $this.Text; $form.Close()
})
$temp.Location = New-Object System.Drawing.Point($buttonX,$buttonY)
$form.Controls.Add($temp)
$index++
}
$shownString = '$this.Activate();'
if ($DefaultChoice -ne -1){
$shownString += '(Get-Variable "button$($DefaultChoice-1)" -ValueOnly).Focus()'
}
$shownSB = [ScriptBlock]::Create($shownString)
$form.Add_Shown($shownSB)
[void]$form.ShowDialog()
$result
}
By this way, you have a better control of the form UI, you can put a blank textblock to make a new line and and in the loop you can create different button control.
Additional reference:
https://gist.github.com/DBremen/73d7999094e7ac342ad6#file-get-choice-ps1
Hope it helps.

Related

Improve multithreading and primary issue excel keeps closing

Primary issue is does anyone know why excel keeps closing? It is random and I do not know how to fix this issue.
My only thougths is to push everything into an array and do one final dump into excel at the end. but hoping for a better option. Or an improvement on how I am coding to help resolve this.
Everything appears to work except for excel randomly closing while processing the printers.
I have this script in multi-threaded form and in single threaded form and i get the same results.
only difference is multi-thread I get more results into the excel file.
Secondary question #2:
i have been reading about a dotnet workspace multithreading option that should be much faster. I am experimenting with but no success yet. Can I get an example where 2 parameters/arguments are passed to each job, with the results returned?
https://adamtheautomator.com/powershell-multithreading/
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.Runspace = $Runspace
$Runspace.Open()
$PowerShell.AddScript({Start-Sleep 5})
$PowerShell.BeginInvoke()
Back to Primary Issue:
Here is my current Script:
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[string[]]$PrintServers = #("Rictx-print-p01","Rictx-print-p02","Rictx-cprint-p1","MHOH-CPRINT-P1","MTHOH-PRINT-P03","CORTX-PRINT-P01","PENFL-PRINT-P01","cdc-print","CN110","DA110","ito100","MarOH-PRINT-P01","mthoh-print-p01","mthoh-print-p02","PO100","SV-OHBA-001","SV-OHBUR-01","SV-OHBUR-02","SV-OHDS-001","sv-ohne-001","SV-OHPA-001","sv-ohtm-001","TE110"),
[string]$SaveAsPath = "\\rictx-script-p2\log\PrintServerReport\PrintServerInfo.xlsx", #\\rictx-script-p2\LOG\PrintServer\RICTX-PRINT-P02
[bool]$CloseReportFile = $false,
[bool]$RemoveSpecialCharacters = $true,
$ConcurrentJobProcess = 50
)
. \\rictx-script-p2\Scripts\Class\Log_Class_v3.ps1
# . \\rictx-script-p2\Scripts\Class\PrinterDetailClass.ps1
. \\rictx-script-p2\Scripts\Module\test\test-applicationrunning.ps1
#Excel Line Increment
$script:intRow = 0
#Prepare where file is being saved
if ($SaveAsPath.Substring(0,2) -eq '\\')
{
$Script_OutputLocationsUNCorDrive = ("\\$((Split-Path -Path "$SaveAsPath" -Parent))").Substring(2, ("\\$((Split-Path -Path "$SaveAsPath" -Parent))").IndexOf(("\\$((Split-Path -Path "$SaveAsPath" -Parent))").Split("\")[5]) + (("\\$((Split-Path -Path "$SaveAsPath" -Parent))").Split("\")[5]).Length -2)
$Script_OutputLocationsFolderPath = ("\\$((Split-Path -Path "$SaveAsPath" -Parent))").Substring(("\\$((Split-Path -Path "$SaveAsPath" -Parent))").IndexOf(("\\$((Split-Path -Path "$SaveAsPath" -Parent))").Split("\")[5]) + (("\\$((Split-Path -Path "$SaveAsPath" -Parent))").Split("\")[5]).Length)
}
else
{
$Script_OutputLocationsUNCorDrive = Split-Path -Path "$SaveAsPath" -Qualifier
$Script_OutputLocationsFolderPath = "$(Split-Path -Path $SaveAsPath -NoQualifier)".Substring(0,"$(Split-Path -Path $SaveAsPath -NoQualifier)".Length - (Split-Path -Path "$SaveAsPath" -Leaf).Length -1)
}
$Script_Log = ([log_class]::new())
$Script_Log.UNCorDrive = $Script_OutputLocationsUNCorDrive
$Script_Log.Folders_From_Root_to_Folder_Contaning_the_Log = "$Script_OutputLocationsFolderPath"
$Script_Log.LogFileName = Split-Path -Path $SaveAsPath -Leaf
$Script_Log.AddDatetoFileName = $true
$Script_Log.Enabled = $True
#$Script_Log.LogWrite("$TSQL")
#rename the saved file into a unique file name
$SaveAsPathFinal = "$($Script_Log.LogFolderPath)$($Script_Log.LogFileName).xlsx"
$PrintersInfo_Scriptblock =
{
Param
(
#$TestOnlyPingableComputers = $true,
[String]$PrinterName, $PrintServer
)
#####################################################################################################################################################
# Get Printer Information #
#####################################################################################################################################################
. \\rictx-script-p2\Scripts\Class\PrinterDetailClass.ps1
Write-host $PrinterName
write-host $PrintServer
write-host "Gathering Information"
$PrinterInfo = [PrinterDetailClass]::new($PrinterName, $PrintServer)
$A=1
return $PrinterInfo
}
FUNCTION processCompletedJobs
{
#####################################################################################################################################################
# Send Printer Information to Excel #
#####################################################################################################################################################
[array] $data = #()
$jobs = Get-Job -State Completed
#$JobsProcessed =
foreach( $job in $jobs )
{
$PrinterInfo = Receive-Job $job -Wait -AutoRemoveJob
#If ($Printer.Name -notlike "Microsoft XPS*")
#{
#$PrinterInfo = [PrinterDetailClass]::new($Printer.Name,$PrintServer)
$Sheet.Cells.Item($intRow, 1) = $PrinterInfo.Server
$Sheet.Cells.Item($intRow, 2) = $PrinterInfo.Name
$Sheet.Cells.Item($intRow, 3) = $PrinterInfo.Location
$Sheet.Cells.Item($intRow, 4) = $PrinterInfo.Comment
$Sheet.Cells.Item($intRow, 5) = $PrinterInfo.IPAddress
$Sheet.Cells.Item($intRow, 6) = $PrinterInfo.MAC
$Sheet.Cells.Item($intRow, 7) = $PrinterInfo.PingbyIP
$Sheet.Cells.Item($intRow, 8) = $PrinterInfo.PingbyPrinterName
$Sheet.Cells.Item($intRow, 9) = $PrinterInfo.Shared
$Sheet.Cells.Item($intRow, 10) = $PrinterInfo.ShareName
$Sheet.Cells.Item($intRow, 11) = $PrinterInfo.PortName
$Sheet.Cells.Item($intRow, 12) = $PrinterInfo.SNMPEnabled
$Sheet.Cells.Item($intRow, 13) = $PrinterInfo.SNMPCommunityString
$Sheet.Cells.Item($intRow, 14) = $PrinterInfo.PortNumber
$Sheet.Cells.Item($intRow, 15) = $PrinterInfo.PrinterStatus
#$Sheet.Cells.Item($intRow, 16) = "Removed"
$Sheet.Cells.Item($intRow, 16) = $PrinterInfo.PrinterModel
$Sheet.Cells.Item($intRow, 17) = $PrinterInfo.DriverName
$Sheet.Cells.Item($intRow, 18) = $PrinterInfo.DriverVersion
$Sheet.Cells.Item($intRow, 19) = $PrinterInfo.Driver
$Sheet.Cells.Item($intRow, 20) = $PrinterInfo.PageTotalCount
$Sheet.Cells.Item($intRow, 21) = $PrinterInfo.PageColorCount
$Sheet.Cells.Item($intRow, 22) = $PrinterInfo.PageBlackCount
$Sheet.Cells.Item($intRow, 23) = $PrinterInfo.PageDuplexCount
$Sheet.Cells.Item($intRow, 24) = $PrinterInfo.PageTotalCountAlt
$Sheet.Cells.Item($intRow, 25) = $PrinterInfo.PageCopyBlackCount
$Sheet.Cells.Item($intRow, 26) = $PrinterInfo.PageCopyColorCount
$script:intRow ++ #move to the next in excel
}
}
# Create new Excel workbook
$Excel = New-Object -ComObject Excel.Application
$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlOpenXMLWorkbook
$Excel.Visible = $True
$Excel = $Excel.Workbooks.Add()
$Sheet = $Excel.Worksheets.Item(1)
$Sheet.Name = "Printer Inventory"
#======================================================
$Sheet.Cells.Item(1,1) = "Print Server"
$Sheet.Cells.Item(1,2) = "Printer Name"
$Sheet.Cells.Item(1,3) = "Location"
$Sheet.Cells.Item(1,4) = "Comment"
$Sheet.Cells.Item(1,5) = "IP Address"
$Sheet.Cells.Item(1,6) = "MAC"
$Sheet.Cells.Item(1,7) = "Ping by IP"
$Sheet.Cells.Item(1,8) = "Ping by Printer Name"
$Sheet.Cells.Item(1,9) = "Shared"
$Sheet.Cells.Item(1,10) = "Share Name"
$Sheet.Cells.Item(1,11) = "Port Name"
$Sheet.Cells.Item(1,12) = "SNMP Enabled"
$Sheet.Cells.Item(1,13) = "SNMP Community String"
$Sheet.Cells.Item(1,14) = "Port Number"
$Sheet.Cells.Item(1,15) = "Printer Status"
#$Sheet.Cells.Item(1,16) = "Printer State - Removed"
$Sheet.Cells.Item(1,16) = "Printer Model"
$Sheet.Cells.Item(1,17) = "Driver Name"
$Sheet.Cells.Item(1,18) = "Driver Version"
$Sheet.Cells.Item(1,19) = "Driver"
$Sheet.Cells.Item(1,20) = "Page Total Count"
$Sheet.Cells.Item(1,21) = "Page Color Count"
$Sheet.Cells.Item(1,22) = "Page Black Count"
$Sheet.Cells.Item(1,23) = "Page Duplex Count"
$Sheet.Cells.Item(1,24) = "Page Total Count Alt"
$Sheet.Cells.Item(1,25) = "Page Copy Black Count"
$Sheet.Cells.Item(1,26) = "Page Copy Color Count"
#=======================================================
$script:intRow = 2
$WorkBook = $Sheet.UsedRange
$WorkBook.Interior.ColorIndex = 40
$WorkBook.Font.ColorIndex = 11
$WorkBook.Font.Bold = $True
$PrintServerCounter = 0
#=======================================================
ForEach ($PrintServer in $PrintServers)
{
#####################################################################################################################################################
# Print Servers #
#####################################################################################################################################################
$PrintServerCounter++
Write-Progress -Activity "Processing Print Server:" -Status "Querying $($PrintServer) PRINTSERVER: $PrintServerCounter of $($PrintServers.Count)" -PercentComplete (($PrintServerCounter/$PrintServers.Count) * 100)
Write-Verbose "$(Get-Date): Working on $PrintServer..."
#$Script_Log.LogWrite("$(Get-Date): Working on $PrintServer...")
$Printers = Get-WmiObject Win32_Printer -ComputerName $PrintServer
Write-Host $Printer.name -ForegroundColor Green
$PrinterCounter = 0
ForEach ($Printer in $Printers)
{
#####################################################################################################################################################
# Printers #
#####################################################################################################################################################
$PrinterCounter++
Write-Progress -Activity "Processing Print Server:" -Status "Querying $($Printer.Name) on $PrintServer PRINTER: $PrinterCounter of $($Printers.Count)" -PercentComplete (($PrinterCounter/$Printers.Count) * 100)
Write-Verbose "$(Get-Date): Working on $Printer..."
#$Script_Log.LogWrite("$(Get-Date): Working on $Printer...")
#Write-Host $Printer.name -ForegroundColor Green
$runningjobs = if ( ((get-job | ? { $_.State -eq "running" }) -ne $null) )
{
if ( ((get-job | ? { $_.State -eq "running" }).Count -ne $null) )
{
(get-job | ? { $_.State -eq "running" }).Count
}
else
{0}
}
else
{0}
$completedjobs = (get-job -State Completed).count
while ( ($runningjobs -gt $ConcurrentJobProcess) -or ($completedjobs -gt $ConcurrentJobProcess) )
{
#do nothing
processCompletedJobs
$Excel = $Excel.SaveAs("$SaveAsPathFinal”,1)
#$Excel.SaveAs ($SaveAsPathFinal, $xlFixedFormat, AccessMode:=xlExclusive,ConflictResolution:=Excel.XlSaveConflictResolution.xlLocalSessionChanges)
$completedjobs = (get-job -State Completed).count
$runningjobs = if ( ((get-job | ? { $_.State -eq "running" }) -ne $null) )
{
if ( ((get-job | ? { $_.State -eq "running" }).Count -ne $null) )
{
(get-job | ? { $_.State -eq "running" }).Count
}
else
{0}
}
else
{0}
}
if ($Printer.Name -notlike "Microsoft*")
{
Start-Job -ScriptBlock $PrintersInfo_Scriptblock -ArgumentList ($Printer.Name),$PrintServer -Name "$($PrintServer)_$($Printer.Name)"
get-job | FL
}
}
$WorkBook.EntireColumn.AutoFit() | Out-Null
}
#####################################################################################################################################################
# Completing Printer Information Gathering #
#####################################################################################################################################################
$runningjobs = if ( ((get-job | ? { $_.State -eq "running" }) -ne $null) )
{
if ( ((get-job | ? { $_.State -eq "running" }).Count -ne $null) )
{
(get-job | ? { $_.State -eq "running" }).Count
}
else
{0}
}
else
{0}
while ($runningjobs -gt 0)
{
write-progress -Activity "Completing Printer Information Gathering" `
-Status "Progress: $($runningjobs) to be COMPLETED" `
-PercentComplete (((get-job).count-$runningjobs)/(get-job).count*100)
processCompletedJobs
$Excel = $Excel.SaveAs("$SaveAsPathFinal”,1)
$runningjobs = if ( ((get-job | ? { $_.State -eq "running" }) -ne $null) )
{
if ( ((get-job | ? { $_.State -eq "running" }).Count -ne $null) )
{
(get-job | ? { $_.State -eq "running" }).Count
}
else
{0}
}
else
{0}
}
$intRow ++
$Sheet.Cells.Item($intRow,1) = "Printer inventory completed"
$Sheet.Cells.Item($intRow,1).Font.Bold = $True
$Sheet.Cells.Item($intRow,1).Interior.ColorIndex = 40
$Sheet.Cells.Item($intRow,2).Interior.ColorIndex = 40
Write-Verbose "$(Get-Date): Completed!"
$Excel = $Excel.SaveAs("$SaveAsPathFinal”,1)
$Excel = $Excel.Saved = $True
if ($CloseReportFile)
{
$Excel.Close()
}

How to add colors to Excel output file in Powershell

I have written a script to export specific registry keys and the sub keys inside it with the server ping response, but my scripts works as expected and I can able to export that to Excel as well.
But I need inputs or some help on how to add the colors to the Excel output column based on the value.
As Ex: in my script I will get ping response as true or false, for True I need to add green colour and for False I need to add Red color in my output, please help me to achieve this with my script.
CODE
## Get full list of servers
$Servers = GC -Path ".\Servers.txt"
## Loop through each server
$Result = foreach ($vm in $Servers) {
## Check the Ping reponse for each server
Write-Host "Pinging Server" $vm
$Ping = Test-Connection -Server $vm -Quiet -Verbose
if ($Ping){Write-host "Server" $vm "is Online" -BackgroundColor Green}
else{Write-host "Unable to ping Server" $vm -BackgroundColor Red}
## Check the Network Share path Accessibility
Write-Host "Checking Share Path on" $vm
$SharePath = Test-Path "\\$vm\E$" -Verbose
if ($SharePath){Write-host "Server" $vm "Share Path is Accessible" -BackgroundColor Green}
else{Write-host "Server" $vm "Share path access failed" -BackgroundColor Red}
Invoke-Command -ComputerName $vm {
## Get ChildItems under HKLM TCPIP Parameter Interface
Get-ChildItem -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces' | ForEach-Object {
Get-ItemProperty -Path $_.PSPath | Where-Object { $_.PsObject.Properties.Name -like 'Dhcp*' }
} | Select-Object -Property #{Name = 'ComputerName'; Expression = {$env:COMPUTERNAME+"."+$env:USERDNSDOMAIN}},
#{Name = 'Ping_Response'; Expression = {if($using:Ping) {'Pinging'} else {'Unable to ping'}}},
#{Name = 'Share_Path_Access'; Expression = {if($using:SharePath) {'Accessible'} else {'Not Accessible'}}},
DhcpIPAddress, #{Name = 'DhcpNameServer'; Expression = {$_.DhcpNameServer -split ' ' -join '; '}},
DhcpServer, #{Name = 'DhcpDefaultGateway'; Expression = {$_.DhcpDefaultGateway -join '; '}}
}}
$Result | Select-Object * -Exclude PS*, RunspaceId | Export-Excel -Path "$PSScriptRoot\TCPIP_Interface_Details.xlsx" -AutoSize -BoldTopRow -FreezeTopRow -TitleBold -WorksheetName TCPIP_Interface_Details
You can use the New-ConditionalText cmdlet to highlight cells containing the specified -Text with the color of our choice. The cmdlet can also take RGB colors. I encourage you to read the documentation on it, there are also many examples:
Get-Help New-ConditionalText
Since I don't have access to your $result object I can only give you an example of how you can do it using a simple example:
$result = 0..10 | ForEach-Object {
[pscustomobject]#{
ComputerName = 'Host' + $_
Ping_Response = ('Not Responding', 'Pinging')[($_ % 2)]
}
}
function RGB ($red, $green, $blue ){
return [System.Double]($red + $green * 256 + $blue * 256 * 256)
}
$fontGreen = RGB 0 97 0
$backGreen = RGB 198 239 206
$condProps = #{
Text = 'Pinging'
ConditionalTextColor = $fontGreen
BackgroundColor = $backGreen
}
$conditionalTrue = New-ConditionalText #condProps
$conditionalFalse = New-ConditionalText -Text 'Not Responding'
$props = #{
AutoSize = $true
InputObject = $result
Path = 'test.xlsx' # => Use your absolute Path here!
TableName = 'myTable'
TableStyle = 'Medium11'
WorksheetName = 'myWorkSheetName'
ConditionalText = $conditionalTrue, $conditionalFalse
}
Export-Excel #props
The end result should look something like this (unfortunately Google Sheets doesn't do it justice):

Powershell - Convert list to UI with Out-Gridview

I have a script that read from excel and let the user to choose a column. The issue is that the list is not readable and I want to show the user the option to choose the version with UI with Out-Gridview
One more thing, I need that the answer will be a number
Here is the script:
$ExcelObject = New-Object -ComObject Excel.Application
$ExcelWorkBook = $ExcelObject.Workbooks.Open($SharePointSiteURL)
$ExcelWorkSheet = $ExcelWorkBook.Sheets.Item("VIP List")
$rowMax = $ExcelWorkSheet.UsedRange.Rows.Count
$colMax = $ExcelWorkSheet.UsedRange.Columns.Count
$columns = [ordered]#{}
for ($col = 1; $col -le $colMax; $col++) {
$name = $ExcelWorkSheet.Cells.Item(1, $col).Value() # assuming the first row has the headers
if ($name -ne $null){
$columns[$name] = $col}
}
$columns.GetEnumerator() | ForEach-Object {
# {0,2} means to write the index number from $_.Value right aligned for two digits
'{0,2}: {1}' -f $_.Value, $_.Name
}
do {
$answer = Read-Host "Please enter the number of the column you want to read from" #. Press Q to exit
# ask this question until the user enters a number or 'Q'
} until ($answer -eq 'Q' -or $answer -match '^\d{1,2}$')
switch ($answer) {
'Q' { break } # user wants to quit
{1..$columns.Count} {
# get the Name from the chosen value
$action = $columns.Keys | Where-Object {$columns["$_"] -eq $answer}
Write-Host "You chose to perform: '$action'" -ForegroundColor Cyan
<# run $action #>
}
}
It looks like this:
To let the user select the tool version using Out-GridView, you need to build an array of objects, like below:
$ExcelObject = New-Object -ComObject Excel.Application
$ExcelWorkBook = $ExcelObject.Workbooks.Open($SharePointSiteURL)
$ExcelWorkSheet = $ExcelWorkBook.Sheets.Item("VIP List")
$rowMax = $ExcelWorkSheet.UsedRange.Rows.Count
$colMax = $ExcelWorkSheet.UsedRange.Columns.Count
# now, have the loop output objects that will be collected in variable $columns
$columns = for ($col = 1; $col -le $colMax; $col++) {
$name = $ExcelWorkSheet.Cells.Item(1, $col).Value() # assuming the first row has the headers
# if $name is not empty or whitespace only
if ($name -match '\S') {
[PsCustomObject]#{
Number = $col
Version = $name
}
}
}
# output to Out-GridView with -PassThru parameter so you can capture the selected item
$answer = ($columns | Out-GridView -Title 'Please select' -PassThru).Number
# if the user did not cancel
if ($answer) {
# get the Name from the chosen value
$action = $columns[$answer -1].Version
Write-Host "You chose to perform: '$action'" -ForegroundColor Cyan
<# run $action #>
}
Please do not forget to remove the used COM objects from memory when the code is done, otherwise they will linger on..
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelWorkSheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelWorkBook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelObject)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

Powershell not writing multiple rows to Excel

Hopefully someone can assist, I'm new to Powershell and I've tried creating a few scripts to automate some internal processes. I think I've jumped into the deep end however and I just can't get the below to work.
Essentially I'm trying to use WMI to call WMI to retrieve data from computers inside the network.
The script uses a list of hostnames from my C:Drive to run the queries against and output the data (well it should anyway.) Can anyone help please?
#Create new Excel workbook and write fields.
$excel = New-Object -ComObject excel.application
$excel.visible = $False
$workbook = $excel.Workbooks.Add()
$workbook.Worksheets.Add() | Out-Null
$excel.DisplayAlerts = $False
$excel.Rows.Item(1).Font.Bold = $true
$Sheet= $workbook.Worksheets.Item(1)
$Sheet.Name = 'Server Information'
$Sheet.Cells.Item(1,1) = "Manufacturer"
$Sheet.Cells.Item(1,2) = "Hostname"
$Sheet.Cells.Item(1,3) = "PC Model"
$Sheet.Cells.Item(1,4) = "Username"
$Sheet.Cells.Item(1,5) = "Serial Number"
$Sheet.Cells.Item(1,6) = "OS Architecture"
$Sheet.Cells.Item(1,7) = "HDD Model"
$Sheet.Cells.Item(1,8) = "Total Disk Size (GB)"
$Sheet.Cells.Item(1,9) = "Physical Memory (GB)"
#Import data from text file.
Write-Host "Enter file path for hostnames list..." -ForegroundColor yellow
$computers = (Read-Host 'Insert File Path')
$computername = Get-Content $computers -ErrorAction "Inquire"
Write-Host ""
Write-Host "Excel workbook generated successfully. Writing data to rows and columns..." -ForegroundColor yellow
Write-Host $computername.count lines of data imported.
Write-Host ""
Write-Host "Starting WMI Querying..." -ForegroundColor yellow
#Loop through the Array and add data into the excel file created.
foreach ($computername in $computers) {
($Manufacturer,$Model,$User,$SerialNumber,$OSType,$DDModel,$DDSize,$RAMSize) = $computername.split('|')
$introw = $Sheet.UsedRange.Rows.Count + 1
$Manufacturer = $cs.Manufacturer
$Model = $cs.Model
$User = $cs.UserName
$SerialNumber = $bios.SerialNumber
$OSType = $os.Architecture
$DDModel = $dd.Model
$DDSize = $dd.Size
$RAMSize = $cs.PhysicalSize
(Get-Content C:\nodes.txt) | ForEach-Object {
$cs = gwmi win32_computersystem | Select-Object Manufacturer,#{Name="PC Model"; Expression = {$cs.Model}},Username,#{Name="Physical Memory (GB)";e={[math]::truncate($_.TotalPhysicalMemory /1GB)}}
$bios = gwmi win32_bios | Select-Object SerialNumber
$os = gwmi win32_OperatingSystem | select-object OSArchitecture
$dd = gwmi win32_DiskDrive | select-object Model,#{Name="Total Disk Size (GB)";e={[math]::truncate($dd.Size /1GB)}}
$Sheet.cells.item($introw, 1) = $Manufacturer
$Sheet.cells.item($introw, 2) = $Model
$Sheet.cells.item($introw, 3) = $User
$Sheet.cells.item($introw, 4) = $SerialNumber
$Sheet.cells.item($introw, 5) = $OSType
$Sheet.cells.item($introw, 6) = $DDModel
$Sheet.cells.item($introw, 7) = $DDSize
$Sheet.cells.item($introw, 8) = $RAMSize
$Sheet.UsedRange.EntireColumn.AutoFit();
}
}
#Write and output to Excel file.
$usedRange = $Sheet.UsedRange
$usedRange.EntireColumn.AutoFit() | Out-Null
$workbook.SaveAs("C:\Machine Inventory.xlsx")
$excel.Quit()
Write-Host ""
Write-Host "Process complete! The data has been exported to C:\Machine Inventory.xlsx" -ForegroundColor yellow
Write-Host ""
Write-Host "Press any key to continue ..." -ForegroundColor yellow
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
You do not increase $introw in your foreach-object script block, so you have your script rewrite one single row over and over. Add $introw+=1 to the end of (Get-Content C:\nodes.txt) | ForEach-Object {...} block. Should do.

Active Directory Filter memberof

I am trying to get all of the CN's out of active directory in order to populate groups based on that name into Sharepoint Services. I can list the "memberof" section but I can not seem to split it using split(",")
$Dom = 'LDAP://OU=External,OU=Users,OU=HomeOffice,DC=mydoman,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
$i=0
# Create a selector and start searching from the Root of AD
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
$adobj= $selector.findall() |`
where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj){
$prop=$person.properties
$i++
Write-host "$($prop.department) - $($prop.sn), $($prop.givenname)"
Write-host $person.properties["memberof"]
}
"Total $i"
Now I get everything I need, but I need some way to filter only the CN's out...
As a general rule, write-host is not the best way to generate output. Ideally, you want to emit objects out of your function and let PowerShell do the formatting for you. This is the more "pipeline friendly" way of doing things. In this case, if you had a function Get-GroupMembers you could pipe it to something like
Get-Person | ft CN
The trick is creating a new object and adding properties to it, or just emitting the DirectoryServices object you are pulling already. To create a new custom object you can do the following:
$obj = new-object psobject
$obj | add-member -membertype noteproperty name $PropName -value $valueToStore
People can use your function and pipe it to format-table, format-list, select-object, group-object, sort-object and a variety of other things. Keith Hill's Effective PowerShell has a great chapter on Output that you might find helpful.
There is also an article by Don Jones on using objects instead of text that is quite good as well.
test1.ps1
#Connet using LDAP
$Dom = 'LDAP://OU=External Accounts,OU=Users,OU=The Office,DC=mydomain,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
#Integer for the loop
$i=0
# Create a selector and start searching from the Root of AD
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
#Find the Groups
$adobj= $selector.findall() |`
where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj){
$prop=$person.properties
$i++
#Write-host "$($prop.department) - $($prop.sn), $($prop.givenname)" -foregroundcolor Magenta
$test = $person.properties["memberof"]
ForEach-Object {
$test`
-replace "CN=OLDLEGACYGROUP",""`
-replace "CN=",""`
-replace ",OU=Sales",""`
-replace ",OU=Some Groups",""`
-replace ",OU=Groups","" `
-replace ",OU=The Office","" `
-replace ",DC=mydomain","" `
-replace ",DC=com","" `
-replace ",","`r`n"
}
}
test2.ps1
# Lets start with a clean slate :)
Clear
# Lets reference the assembly / GAC that we need for this
#region
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$SPSite = New-Object Microsoft.SharePoint.SPSite("https://myintranetorextranetsite.myfqdn.com")
$OpenWeb = $SpSite.OpenWeb("/")
#endregion
# Add some eye candy :)
# region
# I really wanted some old school thing in here :)
write-host " _ ____ ____ " -foregroundcolor Magenta
write-host " / \ | _ \ / ___| _ _ _ __ ___ " -foregroundcolor Magenta
write-host " / _ \ | | | |____\___ \| | | | '_ \ / __|" -foregroundcolor Magenta
write-host " / ___ \| |_| |_____|__) | |_| | | | | (__ " -foregroundcolor Magenta
write-host "|_/ \_\____/ |____/ \__, |_| |_|\___|" -foregroundcolor Magenta
write-host " |___/ " -foregroundcolor Magenta
Write-Host " Version 2.0" -foregroundcolor Red
Write-Host " Build 2009 09-11 21:30" -foregroundcolor Red
Write-host " Created by Mitchell J. Skurnik" -foregroundcolor Red
#endregion
# Create the stopwatch
#region
[System.Diagnostics.Stopwatch] $sw;
$sw = New-Object System.Diagnostics.StopWatch
$sw.Stop()
$sw.Start()
#endregion
# Function to control Adding groups
function creategroup
{
param ([string] $siteurl = "https://myintranetorextranetsite.myfqdn.com")
$site = New-Object Microsoft.SharePoint.SPSite($siteurl)
$web = $site.RootWeb;
$group = $currentgroup;
$perm = "Read";
$owner = "jdoe";
if ($owner -eq "") { $owner = $web.CurrentUser.LoginName }
$exists = $web.SiteGroups | where { $_.Name -eq $group }
if ($exists -eq $null)
{
# Create group
$web.SiteGroups.Add($group, $web.EnsureUser($owner), $null, "");
# Give permissions to the group
$assign = New-Object Microsoft.SharePoint.SPRoleAssignment($web.SiteGroups[$group]);
$assign.RoleDefinitionBindings.Add($web.RoleDefinitions[$perm])
$web.RoleAssignments.Add($assign)
Write-Host -ForegroundColor green "Creating sharepoint group - " $currentgroup;
}
$site.Dispose();
}
# Function to add users to the specified group
function addUser
{
# Open a connection to the sharepoint site and then select the sub site you want
$themail = $prop.mail
$thedisplay = $prop.displayname
# If there are accounts that dont have some info lets populate it
if ($themail -eq "")
{
$themail = "testaccount#myfqdn.com"
}
if ($thedisplay -eq "")
{
$thedisplay = "Account, Test"
}
if ($themail -eq $null)
{
$themail = "testaccount#myfqdn.com"
}
if ($thedisplay -eq $null)
{
$thedisplay = "Account, Test"
}
$TheNewGroup = $OpenWeb.SiteGroups | Where-Object {$_.Name -match $currentGroup}
$TheNewGroup.AddUser("NTAMR\" + $prop.samaccountname,$themail,$prop.displayname,"")
#write-host "Added: " $thedisplay -foregroundcolor Red
}
# Function to remove people - be careful using this script :(
# Also not done
function removeUser
{
#$TheNewGroup = $OpenWeb.SiteGroups | Where-Object {$_.Name -match $currentGroup}
#$TheNewGroup.AddUser("NTAMR\" + $prop.samaccountname,$themail,$prop.displayname,"")
#$TheNewGroup.Remove($LoginToDel)
}
# Now onto the real stuff
Write-host "Searching for Groups" -foregroundcolor Green
# Clear out the existing text file so we have a clean slate
$file = New-Item -type file "C:\location\to\my\folder\allGroups.txt" -Force
# Execute the Group Dump Script
C:\location\to\my\folder\test.ps1 | Out-File -filepath "C:\location\to\my\folder\allGroups.txt" -append
# Clean up the list by removing duplicates and sorting everything
$TextFile = $TextFile = "C:\Powershell\allGroups.txt"
$NewTextFile = "C:\Powershell\allGroups - Sorted.txt"
GC $TextFile | Sort | GU > $NewTextFile
# Use LDAP to connect to Active Directory
#region
$Dom = 'LDAP://OU=External Accounts,OU=Users,OU=The Office,DC=mydomain,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
#endregion
# Create a selector and start searching from the Root of AD
#region
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
#endregion
# Integer to compare file length
$c=0
# Get the Group text file's length and write to scree and variable
$fileLength = [System.IO.File]::ReadAllText($NewTextFile).Split("`n").Count
Write-Host "Found " $fileLength "Groups in Active Directory" -foregroundcolor Magenta
# Integer for thumbing through 'memberOf' in active directory
$d = 0
# Integer for the amount of of users found
$f = 0
# Start a while loop where we read through the entire groups text file
while ($c -le $fileLength)
{
# Increment the line number for the next pass through
$c++
# Grab the first line of text from the groups file (Really the 0th line) and then tell the user
$currentGroup = (Get-Content $NewTextFile)[$c]
# Create the group
CreateGroup
#Write-Host "Created Group: " $currentGroup -foregroundcolor Red
#
Write-host $c "/" $fileLength "`t" $currentGroup -foregroundcolor Red
# Query Active directory and force some commands
$adobj= $selector.findall() | where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj)
{
# Variable for the different properties to reduce fatigue
$prop=$person.properties
# The Department
$department = $prop.department
# Sir Name
$sn = $prop.sn
# Given Name
$gn = $prop.givenname
$un = $prop.samaccountname
# Assign the really long memberof to a variable
$memberof = $person.properties["memberof"]
# Length of memberof
$memberofcount = $test.Count
# Loop for each group the member is in
while ($d -le $memberof.Count)
{
$blah = ForEach-Object{`
$memberof[$d]`
-replace "CN=OLDLEGACYGROUP",""`
-replace "CN=",""`
-replace ",OU=Sales",""`
-replace ",OU=Some Groups",""`
-replace ",OU=Groups","" `
-replace ",OU=The Office","" `
-replace ",DC=mydomain","" `
-replace ",DC=com","" `
}
# Incriment the d
$d++
# Is that user in the group?
if ($blah -eq $currentGroup)
{
# Hey look we found somebody in that group :)
Write-host "`t`t`t" $un -foregroundcolor Magenta
addUser
$f++
}
#elseif ($blah -ne $currentGroup)
#{
# removeUser
#}
else
{
# Oh noes...nobody is in that group...that is strange
}
}
# Are we at the end of what the user has
if ($d -ge $memberofs.Count)
{
# Looks like we are :)
$d=0
}
}
# Display amount of users found
#Write-Host "`t`t`t" $f " user(s) found"
$f = 0
}
# Stop Watch
$sw.Stop()
# Write the compact output to the screen
write-host "Updated in Time: ", $sw.Elapsed.ToString()
#This space is saved for future development

Resources