Improve multithreading and primary issue excel keeps closing - multithreading

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()
}

Related

How to export printer information from a server to text file [duplicate]

This question already has answers here:
PowerShell select range of cells from Excel file and convert to CSV
(2 answers)
Closed 2 years ago.
Quick description of what I am looking for.
We have customers that run our software and on the server there are anywhere up to 80 Zebra QLN420 printers all assigned Static IP Addresses. Looking for a script that will export needed printers information should we when we have to upgrade their existing services. I have found a powershell script that work wonderful that exports to csv file. The issue is that Excel isn't install on any of the customers servers.
So what I am looking to do is export instead to a text file using a comma between each field. I am not a powershell coder by any means.
I also found prnport.vbs on Windows that will display most of the information I need in Port Name, Hostaddress, port number but doesn't return the PrinterName.
Here is the powershell that exports to Excel.
Param (
string]$Printservers = "myServer"
)
# Create new Excel workbook
cls
$Excel = New-Object -ComObject Excel.Application
#==========$Excel.Visible = $True
$Excel = $Excel.Workbooks.Add("C:\Makeports\Something.xls")
$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) = "Driver Name"
$Sheet.Cells.Item(1,7) = "Driver Version"
$Sheet.Cells.Item(1,8) = "Driver"
$Sheet.Cells.Item(1,9) = "Shared"
$Sheet.Cells.Item(1,10) = "Share Name"
#=======================================================
$intRow = 2
$WorkBook = $Sheet.UsedRange
$WorkBook.Interior.ColorIndex = 40
$WorkBook.Font.ColorIndex = 11
$WorkBook.Font.Bold = $True
#=======================================================
# Get printer information
ForEach ($Printserver in $Printservers)
{ $Printers = Get-WmiObject Win32_Printer -ComputerName $Printserver
ForEach ($Printer in $Printers)
{
if ($Printer.Name -notlike "Microsoft XPS*")
{
$Sheet.Cells.Item($intRow, 1) = $Printserver
$Sheet.Cells.Item($intRow, 2) = $Printer.Name
$Sheet.Cells.Item($intRow, 3) = $Printer.Location
$Sheet.Cells.Item($intRow, 4) = $Printer.Comment
If ($Printer.PortName -notlike "*\*")
{ $Ports = Get-WmiObject Win32_TcpIpPrinterPort -Filter "name = '$($Printer.Portname)'" -ComputerName $Printserver
ForEach ($Port in $Ports)
{
$Sheet.Cells.Item($intRow, 5) = $Port.HostAddress
}
}
####################
$Drivers = Get-WmiObject Win32_PrinterDriver -Filter "__path like '%$($Printer.DriverName)%'" -ComputerName $Printserver
ForEach ($Driver in $Drivers)
{
$Drive = $Driver.DriverPath.Substring(0,1)
$Sheet.Cells.Item($intRow, 7) = (Get-ItemProperty ($Driver.DriverPath.Replace("$Drive`:","\\$PrintServer\$Drive`$"))).VersionInfo.ProductVersion
$Sheet.Cells.Item($intRow,8) = Split-Path $Driver.DriverPath -Leaf
}
####################
$Sheet.Cells.Item($intRow, 6) = $Printer.DriverName
$Sheet.Cells.Item($intRow, 9) = $Printer.Shared
$Sheet.Cells.Item($intRow, 10) = $Printer.ShareName
$intRow ++
}
}
$WorkBook.EntireColumn.AutoFit() | Out-Null
}
$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 =
Any help to export it to a comma delimited file?
Here is how I got what I was looking for.
$Printservers = "myServer"
$OutPutFile = (Get-Location).Path + "\allprinters8.dat"
New-Item $OutPutFile -ItemType file
$writer = [System.IO.Streamwriter] $OutputFile
$Text = 'Print Server,Printer Name,Ip Address,Driver Name'
$writer.writeline($Text)
ForEach ($Printserver in $Printservers)
{ $Printers = Get-WmiObject Win32_Printer -ComputerName $Printserver
ForEach ($Printer in $Printers)
{
if ($Printer.Name -notlike "Microsoft XPS*")
{
$Text = $PrintServer + "," + $Printer.Name
If ($Printer.PortName -notlike "*\*")
{ $Ports = Get-WmiObject Win32_TcpIpPrinterPort -Filter "name = '$($Printer.Portname)'" -ComputerName $Printserver
ForEach ($Port in $Ports)
{
$Text =$Text +"," + $Port.HostAddress + "," + $Printer.driverName
$writer.writeline($Text)
}
}
}
}
}
$writer.close()

Powershell GUI buttons arranged perpendicularly

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.

Making a list of computers and their rdp settings

I'm trying to use Powershell to compile a simple excel spreadsheet that lists my computer's ping status's, RPD settings, and Hostnames.
I'm frustrated with the portion of the code that uses excel. I'm not really sure why, but when I run this code I'm getting an multiple errors for calling a method on a null value expression.
$ComputerList = Get-Content C:\Users\\Desktop\RDPSTATUS\ComputerList.txt
$excel_file_path = 'C:\Users\\Desktop\RDPSTATUS\RDPSTATUS.xlsx'
# instantiate EXCEL object
$Excel = New-Object -ComObject Excel.Application
$ExcelWorkBook = $Excel.Workbooks.Open($excel_file_path)
$ExcelWorkSheet = $Excel.Worksheets.item("sheet1")
$ExcelWorksheet.activate()
$row = 0
$col = 0
$ExcelWorkSheet.Cells.Item($row , $col) = "Device Name"
$ExcelWorkSheet.Cells.Item($row , $col++) = "RDP Status"
$ExcelWorkSheet.Cells.Item($row , $col+2) = "ping Status"
$row = 1
foreach ($computername in $ComputerList){
$ExcelWorkSheet.Cells.Item($row , $col) = $computername
$PingStatus = Gwmi Win32_PingStatus -Filter "Address = '$computername'" | Select-Object StatusCode
If ($PingStatus.StatusCode -eq 0)
{
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $computername )
$regKey= $reg.OpenSubKey("System\\CurrentControlSet\\Control\\Terminal Server" ,$true)
$Value = $regkey.GetValue("fDenyTSConnections")
$ExcelWorkSheet.Cells.Item($row , $col+2) = "PING"
}
else {
$ExcelWorkSheet.Cells.Item($row , $col+2) = "NO PING"
}
If ($Value -eq 1){
$ExcelWorkSheet.Cells.Item($row , $col++) = "RDP is Disabled"
}
If ($Value -eq 0){
$ExcelWorkSheet.Cells.Item($row , $col++) = "RDP is Enabled"
}
}
$ExcelWorkBook.Save()
$ExcelWorkBook.Close()
$Excel.Quit([System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel))
Stop-Process -Name EXCEL -Force
You've got a couple immediate issues that I can see:
$ExcelWorkSheet = $Excel.Worksheets.item("sheet1") should be $ExcelWorkbook.Worksheets.item("sheet1"). The worksheet is a property of the workbook, not of the Excel application.
$row and $col beginning values should be 1, not 0.
$ExcelWorksheet.activate() is not necessary.

Error append line to excel file with powershell

I am getting error-messages, if i am running this code. I want to read users active directory data such as username, name, mail and append these informations to an xlsx file for license-tracking and ordering.
$user = Read-Host "Username"
$data = Get-ADUser -Identity $user -Properties * | select SamAccountName,AdminDisplayName,EmailAddress
$user = $data.SamAccountName
$name = $data.AdminDisplayName
$mail = $data.EmailAddress
$clo = Read-Host "Alter Computername (Nur bei Übertrag)"
$cln = Read-Host "Neuer Computername"
if ($clo -ne '') {
$out = "Übertrag: $($user), $($name), $($mail), Alter PC: $($clo), neuer PC: $($cln)"
}else {
$kst = Read-Host "Kostenstelle"
$out = "$($user), $($name), $($mail), PC: $($cln), KST: $kst"}
$excel_file_path = '"##PATHTOFILE#CENSORED#\test.xlsx"'
## Instantiate the COM object
$Excel = New-Object -ComObject Excel.Application
$ExcelWorkBook = $Excel.Workbooks.Open($excel_file_path)
$ExcelWorkSheet = $Excel.WorkSheets.item("Tabelle1")
$ExcelWorkSheet.activate()
## Find the first row where the first 7 columns are empty
$row = ($ExcelWorkSheet.UsedRange.Rows | ? { ($_.Value2 | ? {$_ -eq $null}).Count -eq 7 } | select -first 1).Row
$ExcelWorkSheet.Cells.Item($row,1) = 'COLUMN 1 Text'
$ExcelWorkSheet.Cells.Item($row,2) = 'COLUMN 2 Text'
$ExcelWorkSheet.Cells.Item($row,3) = 'COLUMN 3 Text'
$ExcelWorkSheet.Cells.Item($row,4) = "$out"
$ExcelWorkSheet.Cells.Item($row,5) = 'COLUMN 5 Text'
$ExcelWorkBook.Save()
$ExcelWorkBook.Close()
$Excel.Quit(
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel))
Stop-Process -Name EXCEL -Force
The following instruction does not return any value for $row for me:
$row = ($ExcelWorkSheet.UsedRange.Rows | ? { ($_.Value2 | ? {$_ -eq $null}).Count -eq 7 } | select -first 1).Row
I tried to browse the worksheet until a row where the first 7 columns are empty is found, using a loop. It seems to be working.
$lastrow = $ExcelWorkSheet.UsedRange.Rows.Count
for ($row = 1; $row -le $lastrow; $row ++) {
$test = $true
foreach ($col in 1..7) {$test = $test -and ($ExcelWorkSheet.Cells.Item($row,$col).Value2 -eq $null)}
if ($test) {break}
}

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.

Resources