I'm trying to create a script to make an hardware inventory of my PCs and exporting it within excel.
I have some problem listing hard drive.
Let's suppose one PC has two partions, C with 100 GB and E with 200 GB. I'd like to put drives within a single cell in this way with a carriage return.
C: 100 GB
E: 200 GB
If I want to create an excel file I can do something like this
$a = New-Object -comobject Excel.Application
$a.Visible = $True
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$c.Cells.Item(1,1) = "A value in cell A1."
and I know I can query wmi to list hard drive:
gwmi win32_logicaldisk | ? {$_.drivetype -eq 3} | select deviceid,#{Label="Disk GB"; Expression={[math]::truncate($_.Size / 1GB)}}
but I don't know how to get my desired output. Thanks in advance.
http://www.java2s.com/Code/VBA-Excel-Access-Word/File-Path/GetDriveInformation.htm answers you question
How does this work?
$a = New-Object -comobject Excel.Application
$a.Visible = $True
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$label=gwmi win32_logicaldisk | select deviceid,#{Label="Disk GB"; Expression={"$([math]::truncate($_.Size / 1GB)) GB"}} | ft -auto -HideTableHeaders | out-string
$c.Cells.Item(1,1) = $label
You can iterate over the returned drives from your WMI query and add the deviceid and "Disk GB" results to a string value. Then you can write the resulting string value to the Excel cell. One way to do this would be like this:
$a = New-Object -comobject Excel.Application
$a.Visible = $True
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$outputstring = ""
foreach ($drive in $driveinfo) {
$outputstring += "$($drive.deviceid) $($drive."Disk GB") GB`n"
}
$c.cells.item(1,1) = $outputstring
Related
I am tring to create a script that will output the name of .txt files via for loop that counts the number of files and creates an option to open .txt file from just one click.
$s = (Get-ChildItem -Path C:\*.txt -Name | Measure-Object -Line).Lines
for($i=0; $1 -gt 5 ;$i++)
{
$c = [string[]](Get-ChildItem -Path C:\*.txt -Name)
[void] $objListBox.Items.Add('$i')
Write-Output $c
}
I am stuck with Get-childitem like in $c to get the list of file names into a variable so i can split or get the line for user option.
Thanks in advance
i am not sure what exactly you want from the script,
but here is my approach to set the items in $objListBox
$txtFiles=Get-ChildItem -Path C:\stackoverflow -Recurse -Filter *.txt
#Option 1 FullPath to Items
$txtFiles.fullname | ForEach-Object { $objListBox.Items.Add($_) }
#Option 2 Just the Name to Items
$txtFiles.name | ForEach-Object { $objListBox.Items.Add($_) }
#Option 3 Just the Name without Extentension to Items
$txtFiles.basename | ForEach-Object { $objListBox.Items.Add($_) }
#Count for All Files
$txtFiles.Count
You can use the .ToString
method to convert any given object to a string. If you want $c to be a string, the code you are looking for would look something like this:
$c = (Get-ChildItem -Path C:\*.txt -Name).ToString
Made a form for you that is created with a dynamic size and additionally a scrollbar if there is no space left. buttons that start the txt file and close the form. More comments in the code.
$basePath = "C:\"
$SearchString = Join-Path $basePath "*.txt"
$filenames = #(Get-ChildItem -Path $SearchString -Name | Sort)
$count = $filenames.count
#All you need for System.Windows.Forms to work
Add-Type -AssemblyName System.Windows.Forms
# Variables for generating size
$ButtonHeight = 35
$ButtonWidth = 450
$WindowTitle = "Choose a file"
$BottomSpace = $StartHeight = 10
$LeftSpace = $RightSpace = 30
$CurrentHeight = $Startheight
$FormHeight = 60 + $BottomSpace + $StartHeight + $ButtonHeight * $count
$FormWidth = 20 + $LeftSpace + $RightSpace + $ButtonWidth
# Create the form
$form = New-Object System.Windows.Forms.Form
$form.Text = $WindowTitle
$form.Size = New-Object System.Drawing.Size($FormWidth,$FormHeight)
$form.FormBorderStyle = "Fixed3d" # Sizeable: User may change size - Fixed3d: User may not change size
$form.StartPosition = "CenterScreen"
$Form.AutoScroll = $True # Scrollbars when you need it
$form.Topmost = $true #always on top
$form.MaximizeBox = $false #Allows to maximize window
# Generate the buttons in a foreach and arrange them
foreach ($filename in $filenames) {
$GeneratedButton = New-Object System.Windows.Forms.Button
$GeneratedButton.Location = New-Object System.Drawing.Size($LeftSpace,$CurrentHeight)
$GeneratedButton.Size = New-Object System.Drawing.Size($ButtonWidth,$ButtonHeight)
$GeneratedButton.Text = $filename
# Action to take when button is clicked -- Open file and close form
$GeneratedButton.Add_Click({ Start-Process (Join-Path $BasePath $this.text) ; $form.Close() })
$form.Controls.Add($GeneratedButton)
$CurrentHeight += $ButtonHeight
}
# Activate the Form when loaded
$form.Add_Load({
$form.Activate()
})
# Show the form when loaded, but hide any results
$form.ShowDialog() > $null # Trash any output
I hope someone can help me understand what I am doing wrong.
The script works, if I enter multiple computers or multiple lines, but if I only enter 1 line (1 value). Lets say: Computer 201... the result will be 1
I have disabled the PING feature for now until I can figure out why it does not work with 1 line.
I added the whole code so you can test it yourself.
Thank you
# Load required assemblies
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
# Drawing form and controls
$Harvester = New-Object System.Windows.Forms.Form
$Harvester.Text = "Ping Computers"
$Harvester.Size = New-Object System.Drawing.Size(490,300)
$Harvester.FormBorderStyle = "FixedDialog"
$Harvester.TopMost = $true
$Harvester.MaximizeBox = $false
$Harvester.MinimizeBox = $false
$Harvester.ControlBox = $true
$Harvester.StartPosition = "CenterScreen"
$Harvester.Font = "Segoe UI"
#======================== INPUTBOX - Computers ========================#
$label_message2 = New-Object System.Windows.Forms.Label
$label_message2.Location = New-Object System.Drawing.Size(20,10)
$label_message2.Size = New-Object System.Drawing.Size(100,15)
$label_message2.Text = "Computers"
$Harvester.Controls.Add($label_message2)
# Inputbox
$Inputbox = New-Object System.Windows.Forms.TextBox
$Inputbox.Multiline = $True;
$Inputbox.Location = New-Object System.Drawing.Size(20,30)
$Inputbox.Size = New-Object System.Drawing.Size(200,150)
$Inputbox.ScrollBars = "Vertical"
$Harvester.Controls.Add($Inputbox)
#======================== INPUTBOX - Completed ========================#
$label_message_success = New-Object System.Windows.Forms.Label
$label_message_success.Location = New-Object System.Drawing.Size(250,10)
$label_message_success.Size = New-Object System.Drawing.Size(100,15)
$label_message_success.Text = "Successful"
$Harvester.Controls.Add($label_message_success)
# Inputbox
$Inputbox_success = New-Object System.Windows.Forms.TextBox
$Inputbox_success.Multiline = $True;
$Inputbox_success.Location = New-Object System.Drawing.Size(250,30)
$Inputbox_success.Size = New-Object System.Drawing.Size(200,150)
$Inputbox_success.ScrollBars = "Vertical"
$Harvester.Controls.Add($Inputbox_success)
#======================== Ping ========================#
$button_Ping = New-Object System.Windows.Forms.Button
$button_Ping.Location = New-Object System.Drawing.Size(120,200)
$button_Ping.Size = New-Object System.Drawing.Size(80,32)
$button_Ping.TextAlign = "MiddleCenter"
$button_Ping.Text = "Ping"
$button_Ping.Add_Click({
If ($Inputbox.TextLength -eq 0){
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show('Please enter at 1 computer to perform this action')
Return
}
[collections.arraylist] $ref = #(($Inputbox.Text -split '\r?\n').Trim() -ne '')
if(-not $ref) { return } # if the textbox is empty, don't do anything
$i = 0
1..$ref.Count | ForEach-Object {
#if(Test-Connection $ref[$i] -Quiet) {
$Inputbox_success.Text += $ref[$i] + [environment]::NewLine
$ref.RemoveAt($i)
#}
$Inputbox.Text = $ref | Out-String
$Inputbox, $Inputbox_success | ForEach-Object Refresh
}
})
$Harvester.Controls.Add($button_Ping)
# show form
$Harvester.Add_Shown({$Harvester.Activate()})
[void] $Harvester.ShowDialog()
On a single object you get a value of 1 because of this line:
[collections.arraylist] $ref = #(($Inputbox.Text -split '\r?\n').Trim() -ne '')
More specifically because when a string is evaluated against -ne '' it will return $true or $false, but when you pass an array to that it will instead output all results where that evaluated as true. The way to fix this is to force it to be an array every time. That can be done like this:
[collections.arraylist] $ref = [string[]]($Inputbox.Text -split '\r?\n') -ne ''
That fixes that, but it still leaves other issues, or, at least one other issue I see. You set $i to 0, then loop through things starting at 1, but always reference $i, which will always evaluate to 0, and effectively manipulate item 0 in the $ref array for each thing you evaluate against. So if your If statement succeeds on 3 out of 6 things it will always move the first 3 things over, regardless of what succeeds. To resolve that I changed your code as little as I could, but ended up with this:
0..($ref.Count -1) | ForEach-Object {
# if(Test-Connection $ref[$i] -Quiet) {
if($ref[$_][-1]%2){
$Inputbox_success.Text += $ref[$_] + [environment]::NewLine
}
}
$Inputbox.Text = $ref |Where{$Inputbox_success.Text -notmatch ([regex]::Escape($_))}| Out-String
$Inputbox, $Inputbox_success | ForEach-Object Refresh
So rather than remove things from the array, I rather copy the successes to the $Inputbox_success box, and then rebuild $Inputbox based on the items that are not present in $Inputbox_success at the end. Now, I just set $Inputbox.text to be this:
$Inputbox.Text = #'
Computer201
Computer202
Computer203
Computer204
Computer206
Computer207
'#
And evaluated which name ended in an odd number, but you can comment out my If statement, and re-implement your own to make it actually ping things and go off that.
I need to search for a word in a row from a spreadsheet and update another cell in the same row with a different value. For example, I have the data like this. I need to search for the person "Smith" from the below spreadsheet and update the value of the 'Status' column from 'Enabled' to 'Disabled' for that row.
"Region","Zone","Customer","Process","Status"
"TEST","East","Smith","HR","Disabled"
"TEST","East","Allen","Finance","Enabled"
"TEST","East","Jake","Payroll","Enabled"
I tried regex and few other functions before posting the question. But I can't get them to work.
Thanks.
It's very easy to use Excel with PowerShell:
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$excelFile = 'C:\test\testsheet.xlsx'
$searchFor = 'Smith'
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$excel.ScreenUpdating = $true
$workbook = $excel.Workbooks.Open( $excelFile ,$null, $false )
$ws = $workbook.WorkSheets.item(1)
[void]$ws.Activate()
$searchRange = $ws.UsedRange
$searchResult = $searchRange.Find( $searchFor, [System.Type]::Missing, [System.Type]::Missing,
[Microsoft.Office.Interop.Excel.XlLookAt]::xlWhole,
[Microsoft.Office.Interop.Excel.XlSearchOrder]::xlByColumns,
[Microsoft.Office.Interop.Excel.XlSearchDirection]::xlNext )
while( $searchResult ) {
$row = $searchResult.Row
$col = $searchResult.Column
$ws.Cells( $row, $col + 2 ).Value2 = 'Disabled'
$searchResult = $searchRange.FindNext( $searchResult )
if( $searchResult -and $searchResult.Row -le $row ) {
break
}
}
[void]$workbook.Save()
[void]$workbook.Close()
[void]$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
I got it working using the below script.
$TGTSERVER = "testservwc01"
$name = "ORATDLLSTR"
$input = Invoke-Command -ComputerName "$TGTSERVER" -ScriptBlock {Import-Csv 'C:\test.csv'}
$value = "Disabled"
$Output = foreach ($i in $input) {
if ($i.Process_Instance -match "$name") {$i.Status = "$value"} $i }
$OutArray = $Output | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId, PSShowComputerName
$OutArray | Invoke-Command -ComputerName "$TGTSERVER" -ScriptBlock {Export-Csv 'C:\test.csv' -NoTypeInformation}
if ( $LastExitCode -ge 1)
{
Write-Warning -Message "$Computer : Disable Step failed"
exit 1
}
However the script fails with exit code 1 even though it updates the csv file with the right value on the remote server.
I have found solution which meet my needs... using Powershell
Not the issue mentioned as in topic.. but overall module have a lot of options which might help modify Excel File using PowerShell
https://www.powershellgallery.com/packages/PSWriteExcel/0.1.15
https://github.com/EvotecIT/PSWriteExcel/blob/master/Examples/Run-Example-FindReplace.ps1
Install-Module -Name PSWriteExcel
Import-Module PSWriteExcel -Force
$FilePath = "D:\Excel_test.xlsx"
$FilePathOutput = "D:\Excel_test1.xlsx"
Find-ExcelDocumentText -FilePath $FilePath -Find 'evotec' -Replace -ReplaceWith 'somethingelse' -FilePathTarget $FilePathOutput -OpenWorkBook -Regex -Suppress $true
I'm writing a powershell script that searches for users inside an Active Directory OU and allows me to reset passwords by choosing matches from a list. I found a Tutorial that uses the System.DirectoryServices.DirectoryEntry and System.DirectoryServices.DirectorySearcher, and modified it like so:
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP:\\[REDACTED]")
##ReadSTDIN
$strSearch = Read-Host -Prompt "Search"
$strCat = "(&(objectCategory=User)(Name=*" + $strSearch + "*))"
## Search Object
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strCat
$objSearcher.SearchScope = "Subtree"
#Load Required Properties into the dynObjLink
$objSearcher.PropertiesToLoad.Add("name")
$objSearcher.PropertiesToLoad.Add("userPrincipalName")
$objSearcher.PropertiesToLoad.Add("SamAccountName")
##Magical Search Function
$colResults = $objSearcher.FindAll()
$colResults.PropertiesLoaded
#for every returned userID add them to a table
ForEach ($objResult in $colResults)
{$a++
$objResult.count
$objItem = $objResult.Properties
$objItem.name
$objItem.userPrincipalName
$results.Add($a, $objItem.name + $objItem.userPrincipalName + $objItem.SamAccountName)
}
#Print Table
$results | Format-Table -AutoSize
This works well enough, but when it prints data I can only get the "first name" value of anything that comes back. Everything else becomes NULL and I can't figure out why.
Name Value
---- -----
3 {James3 [REDACTED], $null, $null}
2 {James2 [REDACTED], $null, $null}
1 {James1 [REDACTED], $null, $null}
I've tried different kinds of authentication and manipulating values, but the DirectorySearcher object only seems to collect the "name" value of any record it returns, no matter what I load into it. Help?
Here's a bit shorter (and PowerShell v2-compatible) way of doing this:
#requires -version 2
param(
[Parameter(Mandatory=$true)]
[String] $SearchPattern
)
$searcher = [ADSISearcher] "(&(objectClass=user)(name=$SearchPattern))"
$searcher.PageSize = 1000
$searcher.PropertiesToLoad.AddRange(#("name","samAccountName","userPrincipalName"))
$searchResults = $searcher.FindAll()
if ( $searchResults.Count -gt 0 ) {
foreach ( $searchResult in $searchResults ) {
$properties = $searchResult.Properties
$searchResult | Select-Object `
#{Name = "name"; Expression = {$properties["name"][0]}},
#{Name = "sAMAccountName"; Expression = {$properties["samaccountname"][0]}},
#{Name = "userPrincipalName"; Expression = {$properties["userprincipalname"][0]}}
}
}
$searchResults.Dispose()
Note that there's no need to build a list and output afterwards. Just output each search result. Put this code in a script file and call it:
PS C:\Scripts> .\Searcher.ps1 "*dyer*"
If you omit the parameter, PowerShell will prompt you for it (because the parameter is marked as mandatory).
try using Properties matching to the PropertiesToLoad
$entry = new-object -typename system.directoryservices.directoryentry -ArgumentList $LDAPServer, "ldap", "esildap"
$entry.Path="LDAP://OU=childOU,OU=parentOU,DC=dc1,DC=dc2"
$searcher = new-object -typename system.directoryservices.directorysearcher -ArgumentList $entry
$searcher.PropertiesToLoad.Add('samaccountname')
$searcher.PropertiesToLoad.Add('mail')
$searcher.PropertiesToLoad.Add('displayname')
$objs = $searcher.findall()
foreach($data in $objs)
{
$samaccountname = $data.properties['samaccountname'][0] + ''
$mail = $data.properties['mail'][0] + ''
$displayname = $data.properties['displayname'][0] + ''
}
when accessing the properties of the resultset you get a System.DirectoryServices.ResultPropertyValueCollection type for each property
to get a string value for passing to a database the property value access the zero index of the object
I'm still a beginner with powershell, but love to learn and research all the things it can do.
So with that, I am looking to create a script that will output to a datagridview table. I created a simple enough form that has a multi-line text box where you can enter in a list of servers and then just a simple "check" button that calls my function to actually query each server for their services and outputs that info into the datagridview below it.
That part was easy enough for me to create. My thing is, I want it to do the following":
query each server and pull the list of all services on that server
then in the datagridview, I want it to show the service name in column 1, and then the count of how many servers have that service in column 2
Hope this make since. Can't really provide any code as I don't know how to properly write it! I'm thinking there is someway to generate the full list, then do a loop and count type thing, but not sure.
I thank you for any assistance with this. Been googling and reading up like crazy. Either not correct search criteria or something just isn't clicking for me.
Update - Adding in current script I have:
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$xForm = 800
$yForm = 800
$SVCForm = New-Object System.Windows.Forms.Form
$SVCForm.Text = "Automatic Services Query"
$SVCForm.Size = New-Object System.Drawing.Size($xForm,$yForm)
$SVCForm.FormBorderStyle = "FixedSingle"
$SVCForm.StartPosition = "CenterScreen"
$SVCForm.ControlBox = $true
$SVCForm.KeyPreview = $True
$SVCForm.ShowIcon = $false
$SVCForm.MinimizeBox = $True
$SVCForm.MaximizeBox = $false
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(365,720)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$x=$CancelButton.Text;$SVCForm.Close()})
$SVCForm.Controls.Add($CancelButton)
$ServerListGroup = New-Object System.Windows.Forms.GroupBox
$ServerListGroup.Location = New-Object System.Drawing.Size(5,10)
$ServerListGroup.size = New-Object System.Drawing.Size(780,300)
$ServerListGroup.text = "Enter list of servers you want to check:"
$SVCForm.Controls.Add($ServerListGroup)
$ServerList = New-object System.Windows.Forms.TextBox
$ServerList.Location = New-object System.Drawing.Size(5,25)
$ServerList.Size = New-Object System.Drawing.Size(280,270)
$ServerList.Multiline = $True
$ServerList.ScrollBars = "Vertical"
#$ServerList.add_TextChanged({ONValButton})
$ServerListGroup.Controls.Add($ServerList)
$SVCButton = New-Object System.Windows.Forms.Button
$SVCButton.Location = New-Object System.Drawing.Size(505,140)
$SVCButton.Size = New-Object System.Drawing.Size(180,22)
$SVCButton.Text = "SVC Check"
$SVCButton.Enabled = $True
$SVCButton.Add_Click({SvcCheckCount})
$ServerListGroup.Controls.Add($SVCButton)
$SvcListGroup = New-Object System.Windows.Forms.GroupBox
$SvcListGroup.Location = New-Object System.Drawing.Size(5,330)
$SvcListGroup.size = New-Object System.Drawing.Size(780,380)
$SvcListGroup.text = "Automatic Running/Not-Running Services are listed below:"
$SVCForm.Controls.Add($SvcListGroup)
#$SvcList = New-object System.Windows.Forms.TextBox
#$SvcList.Location = New-object System.Drawing.Size(5,25)
#$SvcList.Size = New-Object System.Drawing.Size(765,350)
#$SvcList.Multiline = $True
#$SvcList.ScrollBars = "Vertical"
#$SvcList.Readonly= $True
#$SvcListGroup.Controls.Add($SvcList)
$SvcGrid = New-Object System.Windows.Forms.DataGridView
$SvcGrid.Location = New-Object System.Drawing.Size(5,15)
$SvcGrid.Size = New-Object System.Drawing.Size(760,360)
$SvcGrid.ColumnHeadersBorderStyle = [System.Windows.Forms.DataGridViewHeaderBorderStyle]::Single
$SvcGrid.CellBorderStyle = [System.Windows.Forms.DataGridViewCellBorderStyle]::Single
$SvcGrid.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::DisableResizing
$SvcGrid.GridColor = [System.Drawing.Color]::Black
$SvcGrid.RowHeadersVisible = $false
$SvcGrid.AllowUserToAddRows = $false
$SvcGrid.AllowUserToResizeColumns = $False
$SvcGrid.AllowUserToResizeRows = $false
$SvcGrid.ColumnHeadersHeight = 23
$SvcGrid.SelectionMode = [System.Windows.Forms.DataGridViewSelectionMode]::FullRowSelect
$Scroll = New-Object System.Windows.Forms.VScrollBar
$Scroll.Dock = [System.Windows.Forms.DockStyle]::Right
$Scroll.width = 18
$Scroll.isAccessible = $false
$SvcGrid.Controls.Add($Scroll)
$SvcGrid.Columns.Add("Name","Service Name") > $null
$SvcGrid.Columns["Name"].Width = 200
$SvcGrid.Columns["Name"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["Name"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["Name"].ReadOnly = $False
$SvcGrid.Columns.Add("StartMode", "Start Mode") > $null
$SvcGrid.Columns["StartMode"].Width = 180
$SvcGrid.Columns["StartMode"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["StartMode"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["StartMode"].ReadOnly = $False
$SvcGrid.Columns.Add("Running","Running") > $null
$SvcGrid.Columns["Running"].Width = 180
$SvcGrid.Columns["Running"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["Running"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["Running"].ReadOnly = $true
$SvcGrid.Columns.Add("NotRunng","Not Running")>$null
$SvcGrid.Columns["NotRunng"].Width = 180
$SvcGrid.Columns["NotRunng"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["NotRunng"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["NotRunng"].ReadOnly = $true
$SvcListGroup.Controls.Add($SvcGrid)
Function SvcCheckCount
{
$SvcGrid.Rows.Clear()
$Servername = $ServerList.Text.Split("`n")|%{$_.trim()}
foreach($Server in $Servername)
{
$Server = $Server.ToUpper()
$svctest = Get-WmiObject Win32_Service | Where-Object {$_.StartMode -eq 'Auto'} | Select-Object Name, Startmode, State
Foreach ($svc in $svctest)
{
$name = $svc.Name
$startmode = $svc.StartMode
$state = $svc.State
If($state-eq"Running"){$SvcGrid.Rows.add($name,$startmode,$state)}
If($state-eq"Stopped"){$SvcGrid.Rows.add($name,$startmode,$null,$state)}
}
}
}
$SVCForm.Topmost = $True
$SVCForm.Add_Shown({$SVCForm.Activate()})
[void] $SVCForm.ShowDialog()
Since you already have the GUI portion of this functioning I am going to focus us the grouping of the data you are looking for. Going into this the one thing I am fuzzy on is why you have the if statements are for but we can work on that if need be. The following is meant to be in place of your foreach loop
$services = #()
foreach($Server in $Servername){
$Server = $Server.ToUpper()
$services += Get-WmiObject Win32_Service -ComputerName $Server -Filter 'StartMode="Auto"' |
Select-Object Name, Startmode, State |
Add-Member -MemberType NoteProperty -Value $Server -Name "Server" -PassThru
}
$services | Group-Object Name | ForEach-Object{
$SvcGrid.Rows.add($_.Name,$_.Count)
}
First thing: you were not using the $server variable in your wmi call so all the results would have been from the one machine you were running this from. Also, I moved the Where into a -Filter so that should speed up processing on remote machines.
Collect all of this information into an array (adding the computer name in case it becomes useful later). Then, using the results from that use Group-Object to get the counts you were looking for. Pipe that into another ForEach-Object to get the results in your grid ( which i have not tested.)
Side note: If you just want the info in a grid you can use Out-GridView
$services | Group-Object Name | Select name,count | Out-GridView