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

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

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

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.

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.

Read Excel data with Powershell and write to a variable

Using PowerShell I would like to capture user input, compare the input to data in an Excel spreadsheet and write the data in corresponding cells to a variable. I am fairly new to PowerShell and can't seem to figure this out. Example would be: A user is prompted for a Store Number, they enter "123". The input is then compared to the data in Column A. The data in the corresponding cells is captured and written to a variable, say $GoLiveDate.
Any help would be greatly appreciated.
User input can be read like this:
$num = Read-Host "Store number"
Excel can be handled like this:
$xl = New-Object -COM "Excel.Application"
$xl.Visible = $true
$wb = $xl.Workbooks.Open("C:\path\to\your.xlsx")
$ws = $wb.Sheets.Item(1)
Looking up a value in one column and assigning the corresponding value from another column to a variable could be done like this:
for ($i = 1; $i -le 3; $i++) {
if ( $ws.Cells.Item($i, 1).Value -eq $num ) {
$GoLiveDate = $ws.Cells.Item($i, 2).Value
break
}
}
Don't forget to clean up after you're done:
$wb.Close()
$xl.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)
I find it preferable to use an OleDB connection to interact with Excel. It's faster than COM interop and less error prone than import-csv. You can prepare a collection of psobjects (one psobject is one row, each property corresponding to a column) to match your desired target grid and insert it into the Excel file. Similarly, you can insert a DataTable instead of a PSObject collection, but unless you start by retrieving data from some data source, PSObject collection way is usually easier.
Here's a function i use for writing a psobject collection to Excel:
function insert-OLEDBData ($file,$sheet,$ocol) {
{
"xlsb$"
{"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$File`";Extended Properties=`"Excel 12.0;HDR=YES;IMEX=1`";"}
"xlsx$"
{"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$File`";Extended Properties=`"Excel 12.0 Xml;HDR=YES;IMEX=1`";"}
}
$OLEDBCon = New-Object System.Data.OleDb.OleDbConnection($cs)
$hdr = $oCol|gm -MemberType NoteProperty|%{$_.name}
$names = '[' + ($hdr-join"],[") + ']'
$vals = (#("?")*([array]$hdr).length)-join','
$sql = "insert into [$sheet`$] ($names) values ($vals)"
$sqlCmd = New-Object system.Data.OleDb.OleDbCommand($sql)
$sqlCmd.connection = $oledbcon
$cpary = #($null)*([array]$hdr).length
$i=0
[array]$hdr|%{([array]$cpary)[$i] = $sqlCmd.parameters.add($_,"VarChar",255);$i++}
$oledbcon.open()
for ($i=0;$i-lt([array]$ocol).length;$i++)
{
for ($k=0;$k-lt([array]$hdr).length;$k++)
{
([array]$cpary)[$k].value = ([array]$oCol)[$i].(([array]$hdr)[$k])
}
$res = $sqlCmd.ExecuteNonQuery()
}
$OLEDBCon.close()
}
This does not seem to work anymore. I swear it used to, but maybe an update to O365 killed it? or I last used it on Win 7, and have long since moved to Win 10:
$GoLiveDate = $ws.Cells.Item($i, 2).Value
I can still use .Value for writing to a cell, but not for reading it into a variable. instead of the contents of the cell, It returns: "Variant Value (Variant) {get} {set}"
But after some digging, I found this does work to read a cell into a variable:
$GoLiveDate = $ws.Cells.Item($i, 2).Text
In regards to the next question / comment squishy79 asks about slowness, and subsequent
OleDB solutions, I can't seem to get those to work in modern OS' either, but my own performance trick is to have all my Excel PowerShell scripts write to a tab delimited .txt file like so:
Add-Content -Path "C:\FileName.txt" -Value $Header1`t$Header2`t$Header3...
Add-Content -Path "C:\FileName.txt" -Value $Data1`t$Data2`t$Data3...
Add-Content -Path "C:\FileName.txt" -Value $Data4`t$Data5`t$Data6...
then when done writing all the data, open the .txt file using the very slow Com "Excel.Application" just to do formatting then SaveAs .xlsx (See comment by SaveAs):
Function OpenInExcelFormatSaveAsXlsx
{
Param ($FilePath)
If (Test-Path $FilePath)
{
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $true
$Workbook = $Excel.Workbooks.Open($FilePath)
$Sheet = $Workbook.ActiveSheet
$UsedRange = $Sheet.UsedRange
$RowMax = ($Sheet.UsedRange.Rows).count
$ColMax = ($Sheet.UsedRange.Columns).count
# This code gets the Alpha character for Columns, even for AA AB, etc.
For ($Col = 1; $Col -le $ColMax; $Col++)
{
$Asc = ""
$Asc1 = ""
$Asc2 = ""
If ($Col -lt 27)
{
$Asc = ([char]($Col + 64))
Write-Host "Asc: $Asc"
}
Else
{
$First = [math]::truncate($Col / 26)
$Second = $Col - ($First * 26)
If ($Second -eq 0)
{
$First = ($First - 1)
$Second = 26
}
$Asc1 = ([char][int]($First + 64))
$Asc2 = ([char][int]($Second + 64))
$Asc = "$Asc1$Asc2"
}
}
Write-Host "Col: $Col"
Write-Host "Asc + 1: $Asc" + "1"
$Range = $Sheet.Range("a1", "$Asc" + "1")
$Range.Select() | Out-Null
$Range.Font.Bold = $true
$Range.Borders.Item(9).LineStyle = 1
$Range.Borders.Item(9).Weight = 2
$UsedRange = $Sheet.UsedRange
$UsedRange.EntireColumn.AutoFit() | Out-Null
$SavePath = $FilePath.Replace(".txt", ".xlsx")
# I found scant documentation, but you need a file format 51 to save a .txt file as .xlsx
$Workbook.SaveAs($SavePath, 51)
$Workbook.Close
$Excel.Quit()
}
Else
{
Write-Host "File Not Found: $FilePath"
}
}
$TextFilePath = "C:\ITUtilities\MyTabDelimitedTextFile.txt"
OpenInExcelFormatSaveAsXlsx -FilePath $TextFilePath
If you don't care about formatting, you can just open the tab delimited .txt files as-is in Excel.
Of course, this is not very good for inserting data into an existing Excel spreadsheet unless you are OK with having the script rewrite the whole sheet it each time an insert is made. It will still run much faster than using COM in most cases.
I found this, and Yevgeniy's answer. I had to do a few minor changes to the above function in order for it to work. Most notably the handeling of NULL or empty valued values in the input array. Here is Yevgeniy's code with a few minor changes:
function insert-OLEDBData {
PARAM (
[Parameter(Mandatory=$True,Position=1)]
[string]$file,
[Parameter(Mandatory=$True,Position=2)]
[string]$sheet,
[Parameter(Mandatory=$True,Position=3)]
[array]$ocol
)
$cs = Switch -regex ($file)
{
"xlsb$"
{"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$File`";Extended Properties=`"Excel 12.0;HDR=YES`";"}
"xlsx$"
{"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$File`";Extended Properties=`"Excel 12.0 Xml;HDR=YES`";"}
}
$OLEDBCon = New-Object System.Data.OleDb.OleDbConnection($cs)
$hdr = $oCol | Get-Member -MemberType NoteProperty,Property | ForEach-Object {$_.name}
$names = '[' + ($hdr -join "],[") + ']'
$vals = (#("?")*([array]$hdr).length) -join ','
$sql = "insert into [$sheet`$] ($names) values ($vals)"
$sqlCmd = New-Object system.Data.OleDb.OleDbCommand($sql)
$sqlCmd.connection = $oledbcon
$cpary = #($null)*([array]$hdr).length
$i=0
[array]$hdr|%{([array]$cpary)[$i] = $sqlCmd.parameters.add($_,"VarChar",255);$i++}
$oledbcon.open()
for ($i=0;$i -lt ([array]$ocol).length;$i++)
{
for ($k=0;$k -lt ([array]$hdr).length;$k++)
{
IF (([array]$oCol)[$i].(([array]$hdr)[$k]) -notlike "") {
([array]$cpary)[$k].value = ([array]$oCol)[$i].(([array]$hdr)[$k])
} ELSE {
([array]$cpary)[$k].value = ""
}
}
$res = $sqlCmd.ExecuteNonQuery()
}
$OLEDBCon.close()
}

Resources