Creating a Powershell Script to get disk info of remote machines - powershell

I have been playing with a script i found online that gives you a breakdown of your disks and a lovely pie chart after.
I was wondering this; we currently have VPN running to all our customer sites where there the servers are.
I can get to them all using the local IP for administration purposes via RDCM or Web Browser. I was wondering if there is anyway for me to be able to run the shell command directly from my machine and listing the ip's of the machines i want checked.
I tried using a list file of the ip's but it just returns nothing.
It would save a lot of time rather than clicking through all 100 servers to get the info every month.
Things to bare in mind:
Each site has it's own domain and admin account/password to access.
Here is the script which is direct from the technet script centre (I havent placed any details in.) It works fine when i run it directly on the machine):
#### Spreadsheet Location
$DirectoryToSaveTo = "c:\"
$date=Get-Date -format "yyyy-MM-d"
$Filename="serverinfo-$($date)"
$FromEmail="<ToEmail>"
$ToEmail="<FromEmail>"
$SMTPMail="<SMTP MAIL>"
###InputLocation
$Computers = Get-Content "c:\server.txt"
# before we do anything else, are we likely to be able to save the file?
# if the directory doesn't exist, then create it
if (!(Test-Path -path "$DirectoryToSaveTo")) #create it if not existing
{
New-Item "$DirectoryToSaveTo" -type directory | out-null
}
#Create a new Excel object using COM
$Excel = New-Object -ComObject Excel.Application
$Excel.visible = $True
$Excel = $Excel.Workbooks.Add()
$Sheet = $Excel.Worksheets.Item(1)
$sheet.Name = 'Server Inventory'
#Create a Title for the first worksheet
$row = 1
$Column = 1
$Sheet.Cells.Item($row,$column)= 'Server Inventory'
$range = $Sheet.Range("a1","s2")
$range.Merge() | Out-Null
$range.VerticalAlignment = -4160
#Give it a nice Style so it stands out
$range.Style = 'Title'
#Increment row for next set of data
$row++;$row++
#Save the initial row so it can be used later to create a border
#Counter variable for rows
$intRow = $row
$xlOpenXMLWorkbook=[int]51
#Read thru the contents of the SQL_Servers.txt file
$Sheet.Cells.Item($intRow,1) ="Name"
$Sheet.Cells.Item($intRow,2) ="status"
$Sheet.Cells.Item($intRow,3) ="OS"
$Sheet.Cells.Item($intRow,4) ="Domain Role"
$Sheet.Cells.Item($intRow,5) ="ProcessorName"
$Sheet.Cells.Item($intRow,6) ="Manufacturer"
$Sheet.Cells.Item($intRow,7) ="Model"
$Sheet.Cells.Item($intRow,8) ="SystemType"
$Sheet.Cells.Item($intRow,9) ="Last Boot Time"
$Sheet.Cells.Item($intRow,10) ="Bios Version"
$Sheet.Cells.Item($intRow,11) ="CPU Info"
$Sheet.Cells.Item($intRow,12) ="NoOfProcessors"
$Sheet.Cells.Item($intRow,13) ="Total Physical Memory"
$Sheet.Cells.Item($intRow,14) ="Total Free Physical Memory"
$Sheet.Cells.Item($intRow,15) ="Total Virtual Memory"
$Sheet.Cells.Item($intRow,16) ="Total Free Virtual Memory"
$Sheet.Cells.Item($intRow,17) ="Disk Info"
$Sheet.Cells.Item($intRow,18) ="FQDN"
$Sheet.Cells.Item($intRow,19) ="IPAddress"
for ($col = 1; $col –le 19; $col++)
{
$Sheet.Cells.Item($intRow,$col).Font.Bold = $True
$Sheet.Cells.Item($intRow,$col).Interior.ColorIndex = 48
$Sheet.Cells.Item($intRow,$col).Font.ColorIndex = 34
}
$intRow++
Function GetStatusCode
{
Param([int] $StatusCode)
switch($StatusCode)
{
0 {"Success"}
11001 {"Buffer Too Small"}
11002 {"Destination Net Unreachable"}
11003 {"Destination Host Unreachable"}
11004 {"Destination Protocol Unreachable"}
11005 {"Destination Port Unreachable"}
11006 {"No Resources"}
11007 {"Bad Option"}
11008 {"Hardware Error"}
11009 {"Packet Too Big"}
11010 {"Request Timed Out"}
11011 {"Bad Request"}
11012 {"Bad Route"}
11013 {"TimeToLive Expired Transit"}
11014 {"TimeToLive Expired Reassembly"}
11015 {"Parameter Problem"}
11016 {"Source Quench"}
11017 {"Option Too Big"}
11018 {"Bad Destination"}
11032 {"Negotiating IPSEC"}
11050 {"General Failure"}
default {"Failed"}
}
}
Function GetUpTime
{
param([string] $LastBootTime)
$Uptime = (Get-Date) - [System.Management.ManagementDateTimeconverter]::ToDateTime($LastBootTime)
"Days: $($Uptime.Days); Hours: $($Uptime.Hours); Minutes: $($Uptime.Minutes); Seconds: $($Uptime.Seconds)"
}
foreach ($Computer in $Computers)
{
TRY {
$OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer
$Bios = Get-WmiObject -Class Win32_BIOS -ComputerName $Computer
$sheetS = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer
$sheetPU = Get-WmiObject -Class Win32_Processor -ComputerName $Computer
$drives = Get-WmiObject -ComputerName $Computer Win32_LogicalDisk | Where-Object {$_.DriveType -eq 3}
$pingStatus = Get-WmiObject -Query "Select * from win32_PingStatus where Address='$Computer'"
$IPAddress=(Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computer | ? {$_.IPEnabled}).ipaddress
$FQDN=[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name
$OSRunning = $OS.caption + " " + $OS.OSArchitecture + " SP " + $OS.ServicePackMajorVersion
$NoOfProcessors=$sheetS.numberofProcessors
$name=$SheetPU|select name -First 1
$Manufacturer=$sheetS.Manufacturer
$Model=$sheetS.Model
$systemType=$sheetS.SystemType
$ProcessorName=$SheetPU|select name -First 1
$DomainRole = $sheetS.DomainRole
$TotalAvailMemory = $OS.totalvisiblememorysize/1kb
$TotalVirtualMemory = $OS.totalvirtualmemorysize/1kb
$TotalFreeMemory = $OS.FreePhysicalMemory/1kb
$TotalFreeVirtualMemory = $OS.FreeVirtualMemory/1kb
$TotalMem = "{0:N2}" -f $TotalAvailMemory
$TotalVirt = "{0:N2}" -f $TotalVirtualMemory
$FreeMem = "{0:N2}" -f $TotalFreeMemory
$FreeVirtMem = "{0:N2}" -f $TotalFreeVirtualMemory
$date = Get-Date
$uptime = $OS.ConvertToDateTime($OS.lastbootuptime)
$BiosVersion = $Bios.Manufacturer + " " + $Bios.SMBIOSBIOSVERSION + " " + $Bios.ConvertToDateTime($Bios.Releasedate)
$sheetPUInfo = $name.Name + " & has " + $sheetPU.NumberOfCores + " Cores & the FSB is " + $sheetPU.ExtClock + " Mhz"
$sheetPULOAD = $sheetPU.LoadPercentage
if($pingStatus.StatusCode -eq 0)
{
$Status = GetStatusCode( $pingStatus.StatusCode )
}
else
{
$Status = GetStatusCode( $pingStatus.StatusCode )
}
if (($DomainRole -eq "0") -or ($DomainRole -eq "1"))
{
$Role = "Work Station"
}
elseif (($DomainRole -eq "2") -or ($DomainRole -eq "3"))
{
$Role = "Member Server"
}
elseif (($DomainRole -eq "4") -or ($DomainRole -eq "5"))
{
$Role = "Domain Controller"
}
else
{
$Role = "Unknown"
}
}
CATCH
{
$pcnotfound = "true"
}
#### Pump Data to Excel
if ($pcnotfound -eq "true")
{
$sheet.Cells.Item($intRow, 1) = "PC Not Found"
}
else
{
$sheet.Cells.Item($intRow, 1) = $computer
$sheet.Cells.Item($intRow, 2) = $status
$sheet.Cells.Item($intRow, 3) = $OSRunning
$sheet.Cells.Item($intRow, 4) = $Role
$sheet.Cells.Item($intRow, 5) = $name.name
$Sheet.Cells.Item($intRow, 6) = $Manufacturer
$Sheet.Cells.Item($intRow, 7) = $Model
$Sheet.Cells.Item($intRow, 8) = $SystemType
$sheet.Cells.Item($intRow, 9) = $uptime
$sheet.Cells.Item($intRow, 10)= $BiosVersion
$sheet.Cells.Item($intRow, 11)= $sheetPUInfo
$sheet.Cells.Item($intRow, 12)=$NoOfProcessors
$sheet.Cells.Item($intRow, 13)= "$TotalMem MB"
$sheet.Cells.Item($intRow, 14)= "$FreeMem MB"
$sheet.Cells.Item($intRow, 15)= "$TotalVirt MB"
$sheet.Cells.Item($intRow, 16)= "$FreeVirtMem MB"
$sheet.Cells.Item($intRow, 19)=$IPAddress
$sheet.Cells.Item($intRow, 18)=$FQDN
$driveStr = ""
foreach($drive in $drives)
{
$size1 = $drive.size / 1GB
$size = "{0:N2}" -f $size1
$free1 = $drive.freespace / 1GB
$free = "{0:N2}" -f $free1
$freea = $free1 / $size1 * 100
$freeb = "{0:N2}" -f $freea
$ID = $drive.DeviceID
$driveStr += "$ID = Total Space: $size GB / Free Space: $free GB / Free (Percent): $freeb % ` "
}
$sheet.Cells.Item($intRow, 17) = $driveStr
}
$intRow = $intRow + 1
$pcnotfound = "false"
}
$erroractionpreference = “SilentlyContinue”
$Sheet.UsedRange.EntireColumn.AutoFit()
########################################333
$Sheet = $Excel.Worksheets.Item(2)
$sheet.Name = 'DiskSpace'
$Sheet.Activate() | Out-Null
#Create a Title for the first worksheet
$row = 1
$Column = 1
$Sheet.Cells.Item($row,$column)= 'Disk Space Information'
$range = $Sheet.Range("a1","h2")
$range.Merge() | Out-Null
$range.VerticalAlignment = -4160
#Give it a nice Style so it stands out
$range.Style = 'Title'
#Increment row for next set of data
$row++;$row++
#Save the initial row so it can be used later to create a border
$initalRow = $row
#Create a header for Disk Space Report; set each cell to Bold and add a background color
$Sheet.Cells.Item($row,$column)= 'Computername'
$Sheet.Cells.Item($row,$column).Interior.ColorIndex =48
$Sheet.Cells.Item($row,$column).Font.Bold=$True
$Column++
$Sheet.Cells.Item($row,$column)= 'DeviceID'
$Sheet.Cells.Item($row,$column).Interior.ColorIndex =48
$Sheet.Cells.Item($row,$column).Font.Bold=$True
$Column++
$Sheet.Cells.Item($row,$column)= 'VolumeName'
$Sheet.Cells.Item($row,$column).Interior.ColorIndex =48
$Sheet.Cells.Item($row,$column).Font.Bold=$True
$Column++
$Sheet.Cells.Item($row,$column)= 'TotalSizeGB'
$Sheet.Cells.Item($row,$column).Interior.ColorIndex =48
$Sheet.Cells.Item($row,$column).Font.Bold=$True
$Column++
$Sheet.Cells.Item($row,$column)= 'UsedSpaceGB'
$Sheet.Cells.Item($row,$column).Interior.ColorIndex =48
$Sheet.Cells.Item($row,$column).Font.Bold=$True
$Column++
$Sheet.Cells.Item($row,$column)= 'FreeSpaceGB'
$Sheet.Cells.Item($row,$column).Interior.ColorIndex =48
$Sheet.Cells.Item($row,$column).Font.Bold=$True
$Column++
$Sheet.Cells.Item($row,$column)= '%Free'
$Sheet.Cells.Item($row,$column).Interior.ColorIndex =48
$Sheet.Cells.Item($row,$column).Font.Bold=$True
$Column++
$Sheet.Cells.Item($row,$column)= 'State'
$Sheet.Cells.Item($row,$column).Interior.ColorIndex =48
$Sheet.Cells.Item($row,$column).Font.Bold=$True
#Set up a header filter
$headerRange = $Sheet.Range("a3","h3")
$headerRange.AutoFilter() | Out-Null
#Increment Row and reset Column back to first column
$row++
$Column = 1
$critical=0
$warning=0
$good=0
#Get the drives and filter out CD/DVD drives
foreach ($computer in $Computers)
{
$diskDrives = Get-WmiObject win32_LogicalDisk -Filter "DriveType='3'" -ComputerName $computer
#Process each disk in the collection and write to spreadsheet
ForEach ($disk in $diskDrives) {
$Sheet.Cells.Item($row,1)= $disk.__Server
$Sheet.Cells.Item($row,2)= $disk.DeviceID
$Sheet.Cells.Item($row,3)= $disk.VolumeName
$Sheet.Cells.Item($row,4)= [math]::Round(($disk.Size /1GB),2)
$Sheet.Cells.Item($row,5)= [math]::Round((($disk.Size - $disk.FreeSpace)/1GB),2)
$Sheet.Cells.Item($row,6)= [math]::Round(($disk.FreeSpace / 1GB),2)
$Sheet.Cells.Item($row,7)= ("{0:P}" -f ($disk.FreeSpace / $disk.Size))
#Determine if disk needs to be flagged for warning or critical alert
If ($disk.FreeSpace -lt 5GB -AND ("{0:P}" -f ($disk.FreeSpace / $disk.Size)) -lt 40) {
$Sheet.Cells.Item($row,8) = "Critical"
$critical++
#Check to see if space is near empty and use appropriate background colors
$range = $Sheet.Range(("A{0}" -f $row),("H{0}" -f $row))
$range.Select() | Out-Null
#Critical threshold
$range.Interior.ColorIndex = 3
} ElseIf ($disk.FreeSpace -lt 10GB -AND ("{0:P}" -f ($disk.FreeSpace / $disk.Size)) -lt 60) {
$Sheet.Cells.Item($row,8) = "Warning"
$range = $Sheet.Range(("A{0}" -f $row),("H{0}" -f $row))
$range.Select() | Out-Null
$warning++
$range.Interior.ColorIndex = 6
} Else {
$Sheet.Cells.Item($row,8) = "Good"
$good++
}
$row++
}
}
#Add a border for data cells
$row--
$dataRange = $Sheet.Range(("A{0}" -f $initalRow),("H{0}" -f $row))
7..12 | ForEach {
$dataRange.Borders.Item($_).LineStyle = 1
$dataRange.Borders.Item($_).Weight = 2
}
#Auto fit everything so it looks better
$usedRange = $Sheet.UsedRange
$usedRange.EntireColumn.AutoFit() | Out-Null
$critical
$warning
$good
$sheet = $excel.Worksheets.Item(2)
$row++;$row++
$beginChartRow = $Row
$Sheet.Cells.Item($row,$Column) = 'Critical'
$Column++
$Sheet.Cells.Item($row,$Column) = 'Warning'
$Column++
$Sheet.Cells.Item($row,$Column) = 'Good'
$Column = 1
$row++
#Critical formula
$Sheet.Cells.Item($row,$Column)=$critical
$Column++
#Warning formula
$Sheet.Cells.Item($row,$Column)=$warning
$Column++
#Good formula
$Sheet.Cells.Item($row,$Column)= $good
$endChartRow = $row
$chartRange = $Sheet.Range(("A{0}" -f $beginChartRow),("C{0}" -f $endChartRow))
##Add a chart to the workbook
#Open a sheet for charts
$temp = $sheet.Charts.Add()
$temp.Delete()
$chart = $sheet.Shapes.AddChart().Chart
$sheet.Activate()
#Configure the chart
##Use a 3D Pie Chart
$chart.ChartType = 70
$chart.Elevation = 40
#Give it some color
$sheet.Shapes.Item("Chart 1").Fill.ForeColor.TintAndShade = .34
$sheet.Shapes.Item("Chart 1").Fill.ForeColor.ObjectThemeColor = 5
$sheet.Shapes.Item("Chart 1").Fill.BackColor.TintAndShade = .765
$sheet.Shapes.Item("Chart 1").Fill.ForeColor.ObjectThemeColor = 5
$sheet.Shapes.Item("Chart 1").Fill.TwoColorGradient(1,1)
#Set the location of the chart
$sheet.Shapes.Item("Chart 1").Placement = 3
$sheet.Shapes.Item("Chart 1").Top = 30
$sheet.Shapes.Item("Chart 1").Left = 600
$chart.SetSourceData($chartRange)
$chart.HasTitle = $True
$chart.ApplyLayout(6,69)
$chart.ChartTitle.Text = "Disk Space Report"
$chart.ChartStyle = 26
$chart.PlotVisibleOnly = $False
$chart.SeriesCollection(1).DataLabels().ShowValue = $True
$chart.SeriesCollection(1).DataLabels().Separator = ("{0}" -f [char]10)
$chart.SeriesCollection(1).DataLabels().Position = 2
#Critical
$chart.SeriesCollection(1).Points(1).Format.Fill.ForeColor.RGB = 255
#Warning
$chart.SeriesCollection(1).Points(2).Format.Fill.ForeColor.RGB = 65535
#Good
$chart.SeriesCollection(1).Points(3).Format.Fill.ForeColor.RGB = 5287936
#Hide the data
$chartRange.EntireRow.Hidden = $True
$sheet.Name = 'DiskInformation'
$filename = "$DirectoryToSaveTo$filename.xlsx"
if (test-path $filename ) { rm $filename } #delete the file if it already exists
$Sheet.UsedRange.EntireColumn.AutoFit()
$Excel.SaveAs($filename, $xlOpenXMLWorkbook) #save as an XML Workbook (xslx)
$Excel.Saved = $True
$Excel.Close()
$Excel.DisplayAlerts = $False
$Excel.quit()
Function sendEmail([string]$emailFrom, [string]$emailTo, [string]$subject,[string]$body,[string]$smtpServer,[string]$filePath)
{
#initate message
$email = New-Object System.Net.Mail.MailMessage
$email.From = $emailFrom
$email.To.Add($emailTo)
$email.Subject = $subject
$email.Body = $body
# initiate email attachment
$emailAttach = New-Object System.Net.Mail.Attachment $filePath
$email.Attachments.Add($emailAttach)
#initiate sending email
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($email)
}
#Call Function
#sendEmail -emailFrom pjayaram#appvion.com -emailTo 'pjayaram#appvion.com,pthangaraj#appvion.com,rmurugesan#appvion.com' -subject "INVENTORY" -body "Windows Server Inventory - COMPLETE DETAILS" -smtpServer hqmail01.appletonpapers.com -filePath $filename
$message = #"
Hi Team,
The Discovery of Windows Server and Disk Space information for all the listed instances.
Autogenerated Email!!! Please do not reply.
Thank you,
Appvion.com
"#
$date=get-date
sendEmail -emailFrom $fromEmail -emailTo $ToEmail -subject "Windows Server Inventory & Disk Details -$($date)" -body $message -smtpServer $SMTPMail -filePath $filename
Many thanks to you all in advance.

For each computer in an external domain, you will have to get the corresponding credentials at the beginning with, for example :
$storedCredential = Get-Credential
Then, when making WMI calls, you will need to specify these credentials, with for example :
$OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer -Credential $storedCredential
$Bios = Get-WmiObject -Class Win32_BIOS -ComputerName $Computer -Credential $storedCredential
$sheetS = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer -Credential $storedCredential

Related

Coding with powershell

I'm new with powershell and i would like to use a loop to ping several Printers on my network.
My problem is : once i'm in the loop of pinging , i can't go out of the loop ...
I tried several things from google but without success ( start-stop , Timer ) . Does anybody have any idea?
Here is the code :
$BtnStartPingClicked = {
if ($LblFileSelectPing.Text -eq "*.txt") {
Add-Type -AssemblyName PresentationCore,PresentationFramework
$ButtonType = [System.Windows.MessageBoxButton]::OK
$MessageIcon = [System.Windows.MessageBoxImage]::Error
$MessageBody = "Please select a list of printer first"
$MessageTitle = "Error"
$Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
Write-Host "Your choice is $Result"
}
else {
do {
$IPList = Get-Content ($LblFileSelectPing.Text)
$snmp = New-Object -ComObject olePrn.OleSNMP
$ping = New-Object System.Net.NetworkInformation.Ping
$i = 11
$j = 1
foreach ($Printer in $IPList) {
try {
$result = $ping.Send($Printer)
} catch {
$result = $null
}
if ($result.Status -eq 'Success') {
$((Get-Variable -name ("GBMachine"+$j+"Ping")).value).Visible = $True
$j++
test-Connection -ComputerName $Printer -Count 1 -Quiet
$printerip = $result.Address.ToString()
# OPEN SNMP CONNECTION TO PRINTER
$snmp.open($Printer, 'public', 2, 3000)
# MODEL
try {
$model = $snmp.Get('.1.3.6.1.2.1.25.3.2.1.3.1')
} catch {
$model = $null
}
# Serial
try {
$serial = $snmp.Get('.1.3.6.1.4.1.1602.1.2.1.8.1.3.1.1').toupper()
} catch {
$Dns = $null
}
# progress
$TBMonitoringPing.SelectionColor = "green"
$TBMonitoringPing.AppendText("$Printer is Pinging")
$TBMonitoringPing.AppendText("`n")
$mac = (arp -a $Printer | Select-String '([0-9a-f]{2}-){5}[0-9a-f]{2}').Matches.Value
# OPEN SNMP CONNECTION TO PRINTER
$((Get-Variable -name ('LblMach' + $i)).value).Text = "IP : $Printerip"
$i++
$((Get-Variable -name ('LblMach' + $i)).value).Text = "Model : $Model"
$i++
$((Get-Variable -name ('LblMach' + $i)).value).Text = "MAC : $mac"
$i++
$((Get-Variable -name ('LblMach' + $i)).value).Text = "Serial : $serial"
$TBAnswerMachine.AppendText("$Model")
$TBAnswerMachine.AppendText("`n")
$TBAnswerMachine.AppendText("$Printer - $Serial")
$TBAnswerMachine.AppendText("`n")
$TBAnswerMachine.AppendText("$Mac")
$TBAnswerMachine.AppendText("`n")
$TBAnswerMachine.AppendText("`n")
Get-Content ($LblFileSelectPing.Text) | Where-Object {$_ -notmatch $Printer} | Set-Content ("C:\_canonsoftware\out.txt")
$i = $i+7
$snmp.Close()
Start-Sleep -milliseconds 1000 # Take a breather!
}
else {
$TBMonitoringPing.selectioncolor = "red"
$TBMonitoringPing.AppendText("$Printer not pinging")
$TBMonitoringPing.AppendText("`n")
Start-Sleep -milliseconds 1000 # Take a breather!
}
}
$LblFileSelectPing.Text = "C:\_canonsoftware\out.txt"
} until($infinity)
}
}
thanks for your answers...
1 - part of my object are indeed not declared in the code because they are in my other PS1 file....
2 - I do a do until infinity because i don't want to stop the code before i decide it...
3 - I didn't explain my problem correctly ( excuse my poor english ) ... i would like to be able to go out of the loop do until at the moment i click on a stop button ... but apprently the windows doens't respond while in the loop ... i have to stop the script with powershell ... which is annoying because i'd like to make an executable with it ... and not have to go out of my program ...
thank you for your ideas

How to send notification mail via powershell

I want to send notification mail after vm deploy like below.
My question is :
1 - If DOMAIN field 'Y' then TRUE else will WORKGROUP
2- IF BACKUP IP field 'N' then it will "not defined"
3- Lastly , I have $diskSizes variable. A VM have one or more disks. $diskSizes[0 -> Hard disk 2] , $diskSizes[1 -> Hard disk 3] and so on.
My script:
$VCServer = Read-Host "Enter the vCenter server name"
Import-Csv -Path C:\temp\vmdeploy.csv -UseCulture -PipelineVariable row |
ForEach-Object -Process {
New-Vm .... blah blah
$diskSizes = #()
do {
$diskSize = Read-Host -Prompt "Additional disk (size in GB or 'no' to stop)"
if($diskSize -ne 'no'){
$diskSizes += $diskSize
}
}
until($diskSize -eq 'no')
if($diskSizes.Count -gt 0){
$diskSizes | %{
New-HardDisk -VM $row.ServerName -CapacityGB $_ | Out-Null
}
}
....
...
$Report = [PSCustomObject]#{
'VMName' = $row.ServerName
'OS' = $row.ServerName
'DOMAIN' = $row.DOMAIN
'LAN IP' = $row.LANIP
'BACKUP IP' = $row.BACKUPIP
'VMState' = (Get-VM -Name $row.ServerName).summary.runtime.powerState
'TotalCPU' = $row.NumCPU
'Totalmemory' = $row.MemoryGB
'vCenter' = $VCServer
'VMHost' = $row.ESXHOST
}
Send-MailMessage .....
}
My CSV File:
ServerName ESXHOST Datastore OSCapacityGB NumCPU MemoryGB NetworkName Second Network Adapter LANIP LANGW BACKUPIP DOMAIN
TestVM01,192.168.30.10,LUNPRDVM01,50,4,16,PG_VLAN_250,Y,10.100.10.12,255.255.255.0,192.168.172.12,Y
TestVM02,192.168.30.11,LUNPRDVM02,60,6,24,PG_VLAN_250,N,10.100.10.13,255.255.255.0,N,N
My desired notification mail :
VMName OS DOMAIN LAN IP BACKUP IP VMState TotalCPU Totalmemory vCenter VMHost Hard disk 2 Hard disk 3
TestVM01 TestVM01 TRUE 10.100.10.12 192.168.172.12 PoweredON 4 16 192.168.100.10 192.168.30.10 50GB not defined
TestVM02 TestVM02 WORKGROUP 10.100.10.13 not defined PoweredON 6 24 192.168.100.10 192.168.30.11 60GB 500GB
The tricky part is where you get the disk sizes in GB from Read-Host.
I would probably maximize the number of disks that can be added and ensure the given value the user typed in is in fact an int.
Something like this:
# set a maximum number of disks to add in this demo no more than 10
$maxDisks = 10
$VCServer = Read-Host "Enter the vCenter server name"
$report = Import-Csv -Path C:\temp\vmdeploy.csv -UseCulture -PipelineVariable row | ForEach-Object {
New-Vm .... blah blah
$newVM = [PSCustomObject]#{
'VMName' = $row.ServerName
'OS' = $row.ServerName
'DOMAIN' = if ($row.DOMAIN -eq 'Y') { 'TRUE' } else { 'WORKGROUP' }
'LAN IP' = $row.LANIP
'BACKUP IP' = if ($row.BACKUPIP -eq 'N') { 'not defined' } else { $row.BACKUPIP }
'VMState' = (Get-VM -Name $row.ServerName).summary.runtime.powerState
'TotalCPU' = $row.NumCPU
'Totalmemory' = $row.MemoryGB
'vCenter' = $VCServer
'VMHost' = $row.ESXHOST
}
# add the harddisk properties to the object, initialize to 'not defined'
for ($i = 1; $i -le $maxDisks; $i++) {
$newVM | Add-Member -MemberType NoteProperty -Name "Hard disk $i" -Value 'not defined'
}
# now ask for the disk sizes
$diskNumber = 1
$intSize = 0 # a variable to use in TryParse()
while ($diskNumber -le $maxDisks) {
$diskSize = Read-Host -Prompt "Additional disk (size in GB or 'no' to stop)"
if ($diskSize -eq 'no') { break } # exit the loop
if ([int]::TryParse($diskSize, [ref]$intSize)) {
New-HardDisk -VM $row.ServerName -CapacityGB $intSize -Confirm:$false | Out-Null
# update the value in the $newVM object
$diskItem = "Hard disk $diskNumber" # the property name in the object
$newVM.$diskItem = '{0}GB' -f $intSize
$diskNumber++ # increment the disk number
}
}
# output the completed object to be collected in the $report variable
$newVM
}
UPDATE
As per your comment you do not want to maximize the number of disks added beforehand, you can change the code to simply keep track of the disks added and afterwards 'fill in the blanks' so you will end up with objects having the same number of properties.
$VCServer = Read-Host "Enter the vCenter server name"
# counter to keep track of the maximum number of disks added
$maxDisks = 0
$report = Import-Csv -Path C:\temp\vmdeploy.csv -UseCulture | ForEach-Object {
Write-Host "Creating new VM: $($_.ServerName)_" -ForegroundColor Yellow
New-Vm .... blah blah
$newVM = [PSCustomObject]#{
'VMName' = $_.ServerName
'OS' = $_.ServerName
'DOMAIN' = if ($_.DOMAIN -eq 'Y') { 'TRUE' } else { 'WORKGROUP' }
'LAN IP' = $_.LANIP
'BACKUP IP' = if ($_.BACKUPIP -eq 'N') { 'not defined' } else { $_.BACKUPIP }
'VMState' = (Get-VM -Name $_.ServerName).summary.runtime.powerState
'TotalCPU' = $_.NumCPU
'Totalmemory' = $_.MemoryGB
'vCenter' = $VCServer
'VMHost' = $_.ESXHOST
}
# ask for the disk sizes and create new disks
$diskNumber = 0
$intSize = 0 # a variable to use in TryParse()
while ($true) {
$diskSize = Read-Host -Prompt "Additional disk (size in GB or 'no' to stop)"
if ($diskSize -eq 'no') { break } # exit the loop
if ([int]::TryParse($diskSize, [ref]$intSize)) {
New-HardDisk -VM $_.ServerName -CapacityGB $intSize -Confirm:$false | Out-Null
# update the value in the $newVM object
$diskNumber++ # increment the disk number
$diskItem = "Hard disk $diskNumber" # the property name in the object
$newVM | Add-Member -MemberType NoteProperty -Name $diskItem -Value ('{0}GB' -f $intSize)
}
}
# update the $maxDisks variable
$maxDisks = [Math]::Max($diskNumber, $maxDisks)
# output the completed object to be collected in the $report variable
$newVM
}
# Your $report variable now holds a collection of VMs with a variable number of properties.
# To be able to create a decent table out of this, you need to add hardisk properties with value 'not defined' where needed.
foreach ($vm in $report) {
$diskCount = ($vm | Select-Object -Property 'Hard disk*').Count
for ($i = $diskCount; $i -lt $maxDisks; $i++) {
$vm | Add-Member -MemberType NoteProperty -Name "Hard disk $($i + 1)" -Value 'not defined'
}
}
# output on screen
$report
After this you can send the report via email, either as plain text or HTML table, exactly the same as in your previous question
I think you're asking how to output $Report as a string for the -body parameter.
You can do so like this:
$body = ($Report | Out-String)

Query and exclude softwares installed on remote computers

I'm using the following powershell function to list the software installed on remote computers and export the data into various formats such as CSV, GridView or console :
Function Get-InstalledApplication
{
Param(
[Parameter(Mandatory=$true)]
[string[]]$Computername,
[String[]]$OutputType,
[string[]]$outpath
)
#Registry Hives
$Object =#()
$excludeArray = ("Security Update for Windows",
"Update for Windows",
"Update for Microsoft .NET",
"Security Update for Microsoft",
"Hotfix for Windows",
"Hotfix for Microsoft .NET Framework",
"Hotfix for Microsoft Visual Studio 2007 Tools",
"Microsoft Visual C++ 2010",
"cwbin64a",
"Hotfix")
[long]$HIVE_HKROOT = 2147483648
[long]$HIVE_HKCU = 2147483649
[long]$HIVE_HKLM = 2147483650
[long]$HIVE_HKU = 2147483651
[long]$HIVE_HKCC = 2147483653
[long]$HIVE_HKDD = 2147483654
Foreach($EachServer in $Computername){
$Query = Get-WmiObject -ComputerName $Computername -query "Select AddressWidth, DataWidth,Architecture from Win32_Processor"
foreach ($i in $Query)
{
If($i.AddressWidth -eq 64){
$OSArch='64-bit'
}
Else{
$OSArch='32-bit'
}
}
Switch ($OSArch)
{
"64-bit"{
$RegProv = GWMI -Namespace "root\Default" -list -computername $EachServer| where{$_.Name -eq "StdRegProv"}
$Hive = $HIVE_HKLM
$RegKey_64BitApps_64BitOS = "Software\Microsoft\Windows\CurrentVersion\Uninstall"
$RegKey_32BitApps_64BitOS = "Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
$RegKey_32BitApps_32BitOS = "Software\Microsoft\Windows\CurrentVersion\Uninstall"
#############################################################################
# Get SubKey names
$SubKeys = $RegProv.EnumKey($HIVE, $RegKey_64BitApps_64BitOS)
# Make Sure No Error when Reading Registry
if ($SubKeys.ReturnValue -eq 0)
{ # Loop through all returned subkeys
ForEach ($Name in $SubKeys.sNames)
{
$SubKey = "$RegKey_64BitApps_64BitOS\$Name"
$ValueName = "DisplayName"
$ValuesReturned = $RegProv.GetStringValue($Hive, $SubKey, $ValueName)
$AppName = $ValuesReturned.sValue
$Version = ($RegProv.GetStringValue($Hive, $SubKey, "DisplayVersion")).sValue
$Publisher = ($RegProv.GetStringValue($Hive, $SubKey, "Publisher")).sValue
$donotwrite = $false
if($AppName.length -gt "0"){
Foreach($exclude in $excludeArray)
{
if($AppName.StartsWith($exclude) -eq $TRUE)
{
$donotwrite = $true
break
}
}
if ($donotwrite -eq $false)
{
$Object += New-Object PSObject -Property #{
Application = $AppName;
Architecture = "64-BIT";
ServerName = $EachServer;
Version = $Version;
Publisher= $Publisher;
}
}
}
}}
#############################################################################
$SubKeys = $RegProv.EnumKey($HIVE, $RegKey_32BitApps_64BitOS)
# Make Sure No Error when Reading Registry
if ($SubKeys.ReturnValue -eq 0)
{
# Loop Through All Returned SubKEys
ForEach ($Name in $SubKeys.sNames)
{
$SubKey = "$RegKey_32BitApps_64BitOS\$Name"
$ValueName = "DisplayName"
$ValuesReturned = $RegProv.GetStringValue($Hive, $SubKey, $ValueName)
$AppName = $ValuesReturned.sValue
$Version = ($RegProv.GetStringValue($Hive, $SubKey, "DisplayVersion")).sValue
$Publisher = ($RegProv.GetStringValue($Hive, $SubKey, "Publisher")).sValue
$donotwrite = $false
if($AppName.length -gt "0"){
Foreach($exclude in $excludeArray)
{
if($AppName.StartsWith($exclude) -eq $TRUE)
{
$donotwrite = $true
break
}
}
if ($donotwrite -eq $false)
{
$Object += New-Object PSObject -Property #{
Application = $AppName;
Architecture = "32-BIT";
ServerName = $EachServer;
Version = $Version;
Publisher= $Publisher;
}
}
}
}
}
} #End of 64 Bit
######################################################################################
###########################################################################################
"32-bit"{
$RegProv = GWMI -Namespace "root\Default" -list -computername $EachServer| where{$_.Name -eq "StdRegProv"}
$Hive = $HIVE_HKLM
$RegKey_32BitApps_32BitOS = "Software\Microsoft\Windows\CurrentVersion\Uninstall"
#############################################################################
# Get SubKey names
$SubKeys = $RegProv.EnumKey($HIVE, $RegKey_32BitApps_32BitOS)
# Make Sure No Error when Reading Registry
if ($SubKeys.ReturnValue -eq 0)
{ # Loop Through All Returned SubKEys
ForEach ($Name in $SubKeys.sNames)
{
$SubKey = "$RegKey_32BitApps_32BitOS\$Name"
$ValueName = "DisplayName"
$ValuesReturned = $RegProv.GetStringValue($Hive, $SubKey, $ValueName)
$AppName = $ValuesReturned.sValue
$Version = ($RegProv.GetStringValue($Hive, $SubKey, "DisplayVersion")).sValue
$Publisher = ($RegProv.GetStringValue($Hive, $SubKey, "Publisher")).sValue
if($AppName.length -gt "0"){
$Object += New-Object PSObject -Property #{
Application = $AppName;
Architecture = "32-BIT";
ServerName = $EachServer;
Version = $Version;
Publisher= $Publisher;
}
}
}}
}#End of 32 bit
} # End of Switch
}
#$AppsReport
$column1 = #{expression="ServerName"; width=15; label="Name"; alignment="left"}
$column2 = #{expression="Architecture"; width=10; label="32/64 Bit"; alignment="left"}
$column3 = #{expression="Application"; width=80; label="Application"; alignment="left"}
$column4 = #{expression="Version"; width=15; label="Version"; alignment="left"}
$column5 = #{expression="Publisher"; width=30; label="Publisher"; alignment="left"}
if ($outputType -eq "Console")
{
"#"*80
"Installed Software Application Report"
"Number of Installed Application count : $($object.count)"
"Generated $(get-date)"
"Generated from $(gc env:computername)"
"#"*80
$object |Format-Table $column1, $column2, $column3 ,$column4, $column5
}
elseif ($OutputType -eq "GridView")
{
$object|Out-GridView
}
elseif ($OutputType -eq "CSV")
{
[string]$FileDS = Get-Date -Format "yyyyMMdd"
[string]$outFile = $outpath+'\'+$computername+'_'+$FileDS+'.csv'
New-Item -ItemType file $outfile -Force
$object | export-csv -path $outfile
}
else
{
write-host " Invalid Output Type $OutputType"
}
}
I'm also using the code to exclude some application from a list. I have a problem on PCs running a 32-bit version of Windows (tested on a 32-bit Windows 7) : applications excluded from this list still appear in the output. No problem on 64-bit systems however.
Ideas of what's wrong ?
Thanks

How do I find the Collection Membership Information with PowerShell

I have the following code, but I get an "Invalid Namespace" error constantly, I am pretty sure I am entering the correct information.
If you have another way of doing this, it's also accepted.
Param(
$SiteCode,
$SourceFile,
$Destination = "$env:USERPROFILE\DESKTOP\Computers.csv"
)
$Computers = Get-Content $SourceFile
$EmptyArray = #()
foreach($computer in $computers)
{
$ResourceIDQuery = Get-WmiObject -Namespace "root\sms\site_$SiteCode" -Class SMS_R_SYSTEM -Filter "Name='$computer'"
$CollectionQuery = Get-WmiObject -Namespace "root\sms\site_$SiteCode" -Class SMS_CollectionMember_a -filter "ResourceID='$($ResourceIDQuery.ResourceId)'"
foreach($Item in $CollectionQuery)
{
$DObject = New-Object PSObject
$Dobject | Add-Member -MemberType NoteProperty -Name "Computer" -Value $computer
$Dobject | Add-Member -MemberType NoteProperty -Name "ResID" -Value $($ResourceIDQuery.ResourceId)
$Dobject | Add-Member -MemberType NoteProperty -Name "CollID" -Value $($Item.CollectionId)
$Dobject | Add-Member -MemberType NoteProperty -Name "DirectOrNot" -Value $($Item.IsDirect)
$EmptyArray += $Dobject
}
}
$EmptyArray | ConvertTo-Csv -NoTypeInformation | Out-File $Destination
Rather than connecting to each computer and extracting the information (slow) get it from the straight from the database....
[CmdletBinding()]
param (
[string] $hosts = "",
[string] $sccmsrv = "",
[Parameter(Mandatory=$False,Position=3)]
[string] $path = ""
)
$usage = "USAGE: List-AdvertCollMembershipSCCM.ps1 -sccmsrv SCCMSERVER -hosts 'host1 host2 host3' -path 'c:\temp\Outfile.csv'"
if ($host -and $sccmsrv){
Write-Host ""
Write-Host -ForegroundColor Yellow "SCCM Server: $sccmsrv"
Write-Host -ForegroundColor Yellow "Looking at hosts: $hosts"
#### Function for executing a SQL query with integrated authentication
function execSQLQuery ([string]$fSQLServer, [string]$db, [string]$query){
$objConnection = New-Object System.Data.SqlClient.SqlConnection
$objConnection.ConnectionString = "Server = $fSQLServer; Database = $db; trusted_connection=true;"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand $query, $objConnection
trap {Write-Host -ForegroundColor 'red' "($sqlsrv/$db not accessible)";continue}
$SqlCmd.Connection.Open()
if ($SqlCmd.Connection.State -ine 'Open') {
$SqlCmd.Connection.Close()
return
}
$dr = $SqlCmd.ExecuteReader()
#get the data
$dt = new-object "System.Data.DataTable"
$dt.Load($dr)
$SqlCmd.Connection.Close()
$dr.Close()
$dr.Dispose()
$objConnection.Close()
return $dt
}
# read the SCCM site name of the SCCM site server
$site = (gwmi -ComputerName $sccmsrv -Namespace root\sms -Class SMS_ProviderLocation).sitecode
# enumerating SQL server name for the given SCCM site server
$sccmCompquery = gwmi -q "Select distinct SiteSystem, Role, SiteCode FROM SMS_SiteSystemSummarizer where role = 'SMS SQL Server' and siteCode = '$site' ORDER BY SiteCode" -namespace "root\sms\site_$site" -ComputerName $sccmsrv
[string]$tmpstr = [regex]::Match($sccmCompquery.sitesystem, "\\\\\w+\\$")
$sccmSQLServer = $tmpstr.replace("\", "")
$objColl = #()
#### Collate the host list.
$hostlist = #($Input)
if ($hosts) {
if($hosts -imatch " "){
$hostsArr = #($hosts.split(" "))
$hostlist += $hostsArr
}
else{
$hostlist += $hosts
}
}
# going through the list of hosts
foreach($srv in $hostlist){
$memberQuery = "SELECT dbo.v_FullCollectionMembership.Name AS 'Hostname', dbo.v_GS_SYSTEM.ResourceID, dbo.v_Collection.Name AS 'Collection Name', dbo.v_Collection.CollectionID, dbo.v_FullCollectionMembership.IsDirect "
$memberQuery += "FROM dbo.v_FullCollectionMembership INNER JOIN dbo.v_Collection ON dbo.v_FullCollectionMembership.CollectionID = dbo.v_Collection.CollectionID INNER JOIN dbo.v_GS_SYSTEM ON dbo.v_FullCollectionMembership.ResourceID = dbo.v_GS_SYSTEM.ResourceID "
$memberQuery += "WHERE (LOWER(dbo.v_FullCollectionMembership.Name) = LOWER('$srv'))"
# running sql query to enumerate list of collections the computer is member of
$membership = execSQLQuery $sccmSQLServer "SMS_$site" $memberQuery
# if we have a result, go through it and build an object collection with the computer name and the collection(s) it is member of
if($membership){
foreach($enumColl in $membership){
$sObject = $enumColl | select Hostname, ResourceID, "Collection Name", CollectionID, IsDirect
$objColl +=$sObject
}
}
}
if ($objColl){
if ($path){
$objColl | ft -AutoSize
Write-Host -ForegroundColor Yellow "Exporting to results to: $path"
$objColl | Export-Csv $path -NoTypeInformation
}
else{
$objColl | ft -AutoSize
Write-Host -ForegroundColor Green "Use the -path argument in the command line to export output to csv to display"
Write-Host -ForegroundColor Green "the 'IsDirect' Information"
Write-Host ""
}
}
Else {
foreach ($Hostname in $hostlist){
Write-Host ""
Write-Host -ForegroundColor Yellow "The host $hostname is not a member of any collection"
}
Write-Host -ForegroundColor Yellow "Check you have entered the correct hostname and try again"
}
}
else {
Write-Host ""
Write-Host -ForegroundColor Yellow $usage
}
Execution:-
PS C:\> ListSCCMCollections.ps1 -sccmsrv SCCMSERVER -hosts host1,host2,host3 -path "c:\temp\Outfile.csv"
or
PS C:\> Get-Content hostlist.txt | ListSCCMCollections.ps1 -sccmsrv SCCMSERVER -path c:\temp\Outfile.csv
Getting the requested info straight from SQL:-
SELECT dbo.v_FullCollectionMembership.Name AS 'Hostname', dbo.v_GS_SYSTEM.ResourceID, dbo.v_Collection.Name AS 'Collection Name', dbo.v_Collection.CollectionID,
dbo.v_FullCollectionMembership.IsDirect
FROM dbo.v_FullCollectionMembership INNER JOIN
dbo.v_Collection ON dbo.v_FullCollectionMembership.CollectionID = dbo.v_Collection.CollectionID INNER JOIN
dbo.v_GS_SYSTEM ON dbo.v_FullCollectionMembership.ResourceID = dbo.v_GS_SYSTEM.ResourceID
WHERE (LOWER(dbo.v_FullCollectionMembership.Name) = LOWER('Hostname'))
This script can be used with any SQL query on the SCCM database. All you need to do is update the SQL query in the script. i.e. the $memberQuery array (if you spread the query over a couple of lines like below, be sure to leave a space at the end of each line with exception to the last).
For example; If you'd like the script to show the clients collections with live advertisements assigned to them change the SQL query in the $memberQuery array to:-
$memberQuery = "SELECT dbo.v_FullCollectionMembership.Name AS 'Hostname', dbo.v_GS_SYSTEM.ResourceID, dbo.v_Collection.Name AS 'Collection Name',dbo.v_Collection.CollectionID, dbo.v_FullCollectionMembership.IsDirect, dbo.v_Advertisement.AdvertisementID, dbo.v_Advertisement.AdvertisementName "
$memberQuery += "FROM dbo.v_FullCollectionMembership INNER JOIN dbo.v_Collection ON dbo.v_FullCollectionMembership.CollectionID = dbo.v_Collection.CollectionID INNER JOIN dbo.v_GS_SYSTEM ON dbo.v_FullCollectionMembership.ResourceID = dbo.v_GS_SYSTEM.ResourceID INNER JOIN dbo.v_Advertisement ON dbo.v_Collection.CollectionID = dbo.v_Advertisement.CollectionID "
$memberQuery += "WHERE (LOWER(dbo.v_FullCollectionMembership.Name) = LOWER('$srv'))"
and the $sObject variable to:-
$sObject = $enumColl | select Hostname, ResourceID, "Collection Name", CollectionID, IsDirect, AdvertisementID, AdvertisementName
Complete script to view client collections with live advisements (execution the same as before):-
[CmdletBinding()]
param (
[string] $hosts = "",
[string] $sccmsrv = "",
[Parameter(Mandatory=$False,Position=3)]
[string] $path = ""
)
$usage = "USAGE: List-AdvertCollMembershipSCCM.ps1 -sccmsrv SCCMSERVER -hosts 'host1 host2 host3' -path 'c:\temp\Outfile.csv'"
if ($host -and $sccmsrv){
Write-Host ""
Write-Host -ForegroundColor Yellow "SCCM Server: $sccmsrv"
Write-Host -ForegroundColor Yellow "Looking at hosts: $hosts"
#### Function for executing a SQL query with integrated authentication
function execSQLQuery ([string]$fSQLServer, [string]$db, [string]$query){
$objConnection = New-Object System.Data.SqlClient.SqlConnection
$objConnection.ConnectionString = "Server = $fSQLServer; Database = $db; trusted_connection=true;"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand $query, $objConnection
trap {Write-Host -ForegroundColor 'red' "($sqlsrv/$db not accessible)";continue}
$SqlCmd.Connection.Open()
if ($SqlCmd.Connection.State -ine 'Open') {
$SqlCmd.Connection.Close()
return
}
$dr = $SqlCmd.ExecuteReader()
#get the data
$dt = new-object "System.Data.DataTable"
$dt.Load($dr)
$SqlCmd.Connection.Close()
$dr.Close()
$dr.Dispose()
$objConnection.Close()
return $dt
}
# read the SCCM site name of the SCCM site server
$site = (gwmi -ComputerName $sccmsrv -Namespace root\sms -Class SMS_ProviderLocation).sitecode
# enumerating SQL server name for the given SCCM site server
$sccmCompquery = gwmi -q "Select distinct SiteSystem, Role, SiteCode FROM SMS_SiteSystemSummarizer where role = 'SMS SQL Server' and siteCode = '$site' ORDER BY SiteCode" -namespace "root\sms\site_$site" -ComputerName $sccmsrv
[string]$tmpstr = [regex]::Match($sccmCompquery.sitesystem, "\\\\\w+\\$")
$sccmSQLServer = $tmpstr.replace("\", "")
$objColl = #()
#### Collate the host list.
$hostlist = #($Input)
if ($hosts) {
if($hosts -imatch " "){
$hostsArr = #($hosts.split(" "))
$hostlist += $hostsArr
}
else{
$hostlist += $hosts
}
}
# going through the list of hosts
foreach($srv in $hostlist){
$memberQuery = "SELECT dbo.v_FullCollectionMembership.Name AS 'Hostname', dbo.v_GS_SYSTEM.ResourceID, dbo.v_Collection.Name AS 'Collection Name',dbo.v_Collection.CollectionID, dbo.v_FullCollectionMembership.IsDirect, dbo.v_Advertisement.AdvertisementID, dbo.v_Advertisement.AdvertisementName "
$memberQuery += "FROM dbo.v_FullCollectionMembership INNER JOIN dbo.v_Collection ON dbo.v_FullCollectionMembership.CollectionID = dbo.v_Collection.CollectionID INNER JOIN dbo.v_GS_SYSTEM ON dbo.v_FullCollectionMembership.ResourceID = dbo.v_GS_SYSTEM.ResourceID INNER JOIN dbo.v_Advertisement ON dbo.v_Collection.CollectionID = dbo.v_Advertisement.CollectionID "
$memberQuery += "WHERE (LOWER(dbo.v_FullCollectionMembership.Name) = LOWER('$srv'))"
# running sql query to enumerate list of collections the computer is member of
$membership = execSQLQuery $sccmSQLServer "SMS_$site" $memberQuery
# if we have a result, go through it and build an object collection with the computer name and the collection(s) it is member of
if($membership){
foreach($enumColl in $membership){
$sObject = $enumColl | select Hostname, ResourceID, "Collection Name", CollectionID, IsDirect, AdvertisementID, AdvertisementName
$objColl +=$sObject
}
}
}
if ($objColl){
if ($path){
$objColl | ft -AutoSize
Write-Host -ForegroundColor Yellow "Exporting to results to: $path"
$objColl | Export-Csv $path -NoTypeInformation
}
else{
$objColl | ft -AutoSize
Write-Host -ForegroundColor Green "Use the -path argument in the command line to export output to csv to display"
Write-Host -ForegroundColor Green "the header 'AdvertisementName'"
Write-Host ""
}
}
Else {
foreach ($Hostname in $hostlist){
Write-Host ""
Write-Host -ForegroundColor Yellow "The host $hostname is not a member of any collection with live advertisements"
}
Write-Host -ForegroundColor Yellow "Check you have entered the correct hostname and try again"
}
}
else {
Write-Host ""
Write-Host -ForegroundColor Yellow $usage
}
And if you connect to SCCM from station with only SCCM Console installed and every other SCCM cmdlets works, trying Get-WmiObject:
Get-WmiObject -Namespace "root\sms\site_$SiteCode" ...
you will have an error:
“Get-WmiObject : Invalid namespace …”
In this situation you should specify parameter ComputerName and point to server where SCCM is installed:
Get-WmiObject -ComputerName "SCCMserver" -Namespace "root\sms\site_$SiteCode" ...
Hope it helps, I waste few minutes by this.

Powershell Timeout After two Seconds

I'm new to powershell. I read some lines on www.powershell.com. Now I need your help to solve a problem. I want to read the UUID from clients in the Network. Therefore I created a document "pcs.txt" where all PCs are stored.
$pc = Get-Content pcs.txt #Read content of file
$cred = Get-Credential “domain\user”
for ($i=0; $i -lt $pc.length; $i++) {
$Result=test-connection -ComputerName $pc[$i] -Count 1 -Quiet
If ($Result -eq 'True')
{
$uuid = (Get-WmiObject Win32_ComputerSystemProduct -ComputerName $pc[$i] -Credential $cred).UUID
$Ausgabe=$pc[$i] + ';'+$uuid
$Ausgabe
}
else
{
$Ausgabe=$pc[$i] + '; UUID nicht erhalten'
$Ausgabe
}
}
First I test if the ping works. When the ping works I try to get the uuid.
Sometimes I don't get the uuid even if the ping worked. So I would like to code a timeout, which say -> go to next pc when you don't have the uuid after 2 seconds.
Can you help me please?
Alas, there is no timeout parameter for Get-WmiObject commandlet. There is a feature request in MS Connect, but it is from 2011 and still open.
A workaround, which I haven't tested is available by using System.Management. I'll copy-and-paste it here in case the link goes dead. (And I hate SO answers that only contain links to resouces that may or may not exist...)
Function Get-WmiCustom([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15){
$ConnectionOptions = new-object System.Management.ConnectionOptions
$EnumerationOptions = new-object System.Management.EnumerationOptions
$timeoutseconds = new-timespan -seconds $timeout
$EnumerationOptions.set_timeout($timeoutseconds)
$assembledpath = "\\" + $computername + "\" + $namespace
#write-host $assembledpath -foregroundcolor yellow
$Scope = new-object System.Management.ManagementScope $assembledpath, $ConnectionOptions
$Scope.Connect()
$querystring = "SELECT * FROM " + $class
#write-host $querystring
$query = new-object System.Management.ObjectQuery $querystring
$searcher = new-object System.Management.ManagementObjectSearcher
$searcher.set_options($EnumerationOptions)
$searcher.Query = $querystring
$searcher.Scope = $Scope
trap { $_ } $result = $searcher.get()
return $result
}
I found a good workaround!
http://theolddogscriptingblog.wordpress.com/2012/05/11/wmi-hangs-and-how-to-avoid-them/
Here my working code:
$pc = Get-Content pcs.txt #FILE FROM THE HARDDISK
$cred = Get-Credential “DOMAIN\USER” #
for ($i=0; $i -lt $pc.length; $i++)
{
$Result=test-connection -ComputerName $pc[$i] -Count 1 -Quiet
If ($Result -eq 'True')
{
$WMIJob = Get-WmiObject Win32_ComputerSystemProduct -ComputerName $pc[$i] -Credential $cred -AsJob
$Timeout=Wait-Job -ID $WMIJob.ID -Timeout 1 # the Job times out after 1 seconds.
$uuid = Receive-Job $WMIJob.ID
if ($uuid -ne $null)
{
$Wert =$uuid.UUID
$Ausgabe=$pc[$i] + ';'+$Wert
$Ausgabe
}
else
{
<#$b = $error | select Exception
$E = $b -split (:)
$x = $E[1]
$Error.Clear() #>
$Ausgabe=$pc[$i] + '; got no uuid'
$Ausgabe
}
}
else
{
$Ausgabe='PC not reached through ping.'
$Ausgabe
}
}
I hope I can help somebody with that