PowerCLI Get-VM output - powershell

I am working on a script, and I am having trouble with a section of code. When I pass in a list of computers that are all found in VCenter, the script populates the $result object correctly with a list of servers and all of the information included. If there are any errors (unable to find server in VCenter) the only thing that is returned is the error line (in the case of multiple errors, only the last error is in $result). Any ideas what I can do to resolve this?
I know that it will work if I enclose the Get-VM statement in a foreach loop, but passing one server at a time to the VCenter takes a very long time.
try {
$operation = Get-VM -Name $computers -ErrorAction Stop | Restart-VMGuest -Confirm:$false
foreach ($comp in $operation) {
$result += [pscustomobject] #{
Server = $computer
Status = $True
Error = $False
ErrorMessage = $null
Stamp = Get-Date
}
}
}
catch {
$result += [pscustomobject] #{
Server = $computer
Status = $False
Error = $True
ErrorMessage = $_
Stamp = Get-Date
}
}

I think the try / catch is on the false position. Maybe if you put an if / else statement in the foreach this work? But I don't know how to check if $comp is en error...
$operation = Get-VM -Name $computers -ErrorAction Stop | Restart-VMGuest -Confirm:$false
foreach ($comp in $operation) {
If ($comp -eq "??error??") {
$result += [pscustomobject] #{
Server = $computer
Status = $True
Error = $False
ErrorMessage = $null
Stamp = Get-Date
}
}
Else {
$result += [pscustomobject] #{
Server = $computer
Status = $False
Error = $True
ErrorMessage = $_
Stamp = Get-Date
}
}
}

Related

Powershell - How come my Ciminstance query is not working and jumping straight to Error Status 1?

Below is a block of my code where I "believe" it is failing out because it's giving me the Error Status [1] in my HTMLOutput File. I'm Fairly new to Powershell and scripting, so I'm not sure if the Query is correct.
I'm trying to query a list of computers is a Text File, to see if it has the PNPDeviceID Like 'USB\VID_0403&PID_6001%'", this will give me an idea of what computers are running those USB devices.
if ($IsPhysicalMachine -eq $false)
{
Try
{
if (-not ($Test))
{ $Query = Get-CimInstance -ComputerName $ComputerName -Query "Select * FROM PNPDevicID LIKE 'USB\\VID_0403&PID_6001%'" -Namespace root\CIMV2 -ErrorAction Stop
}
Else
{ $Query = Get-CimInstance -Query "Select * FROM PNPDevicID LIKE 'USB\\VID_0403&PID_6001%'" -ErrorAction Stop
}
ForEach ($Device in $Query)
{
$Results += New-Object PSObject -Property #{
ComputerName = $ComputerName
Active = $ComputerName.Active
USBDevice = $ComputerName.Device
Status = "0 - OK"
Message = "N/A"
}
}
Continue
}
Catch
{
$Results += New-Object PSObject -Property #{
ComputerName = $ComputerName
Active = "N/A"
USBDevice = "N/A"
Status = "1 - Error"
Message = "Error: $($Error[0])"
}
}
}

powershell script - TimeGenerated Last 24 hours

I have a functional PowerShell script that I'm using to capture user logons and logoffs for single local machine. The script works fine, but I'm having a difficult time trying to pull the last 24 hours from current date/time. I have $Date = [DateTime]::Now.AddDays(-1) at the top of my script, but it appears to be getting ignored.
Can anyone tell me what I'm missing?
powershell
$Date = [DateTime]::Now.AddDays(-1)
$Date.tostring("MM-dd-yyyy_HH,mm,ss")
$UserProperty = #{n="user";e={(New-Object System.Security.Principal.SecurityIdentifier $_.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])}}
$TypeProperty = #{n="Action";e={if($_.EventID -eq 7001) {"Logon"} else {"Logoff"}}}
$TimeProperty = #{n="Time";e={TimeGenerated}}
Get-EventLog System -Source Microsoft-Windows-Winlogon | select $UserProperty,$TypeProperty,$TimeProperty | export-csv -path C:\Temp\TrackLogin.csv -NoTypeInformation
Your current code is very limited as it doesn't interact with the date at all. Here is a bit of code I use in a function. Should work if run as an admin.
$Days = 1
$Computer = $env:COMPUTERNAME
$events = #()
$events += Get-WinEvent -ComputerName $Computer -FilterHashtable #{
LogName='Security'
Id=#(4800,4801)
StartTime=(Get-Date).AddDays(-$Days)
}
$events += Get-WinEvent -ComputerName $Computer -FilterHashtable #{
LogName='System'
Id=#(7001,7002)
StartTime=(Get-Date).AddDays(-$Days)
}
$type_lu = #{
7001 = 'Logon'
7002 = 'Logoff'
4800 = 'Lock'
4801 = 'Unlock'
}
$ns = #{'ns'='http://schemas.microsoft.com/win/2004/08/events/event'}
$target_xpath = "//ns:Data[#Name='TargetUserName']"
$usersid_xpath = "//ns:Data[#Name='UserSid']"
If($events) {
$results = ForEach($event in $events) {
$xml = $event.ToXml()
Switch -Regex ($event.Id) {
'4...' {
$user = (Select-Xml -Content $xml -Namespace $ns -XPath $target_xpath).Node.'#text'
Break
}
'7...' {
$sid = (Select-Xml -Content $xml -Namespace $ns -XPath $usersid_xpath).Node.'#text'
$user = (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList $sid).Translate([System.Security.Principal.NTAccount]).Value
Break
}
}
New-Object -TypeName PSObject -Property #{
Time = $event.TimeCreated
Id = $event.Id
Type = $type_lu[$event.Id]
User = $user
}
}
If($results) {
$results | sort Time -Descending
}
}

Powershell - Login/Logoff Events - Get-WinEvent vs Get-EventLog

I've been working on a script to pull logon/logoff history out of the Event logs. The issue is that almost every code example I found uses "Get-EventLog" which does work, but is extremely slow processing due to the event logs found on a server being a lot larger then a typical workstation. So instead I opted for "Get-WinEvent", as it provides very similar results but returns the results within a few seconds. The issue I am having though is manipulating the output of said results into a usable form.Any assistance is GREATLY appreciated!
Update: I was able to slowly work through the issue on my own. See comments for details. Thanks for anyone who was looking into this :)
Original Working Code (Get-EventLog - Slow Output)
function get-logonhistory{
cls
$Result = #()
Write-Host "Gathering Event Logs, this can take awhile..."
$ELogs = Get-EventLog System -Source Microsoft-Windows-WinLogon
If ($ELogs)
{ Write-Host "Processing..."
ForEach ($Log in $ELogs)
{ If ($Log.InstanceId -eq 7001)
{ $ET = "Logon"
}
ElseIf ($Log.InstanceId -eq 7002)
{ $ET = "Logoff"
}
Else
{ Continue
}
$Result += New-Object PSObject -Property #{
Time = $Log.TimeWritten
'Event Type' = $ET
User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])
}
}
$Result | Select Time,"Event Type",User | Sort Time -Descending | Out-GridView
Write-Host "Done."
}
Else
{ Write-Host "There was a problem reading the logs..."
}
}
get-logonhistory
(Get-WinEvent) - Almost Instant Results
function get-logonhistory{
cls
$Result = #()
Write-Host "Gathering Event Logs, this can take awhile..."
$ELogs = Get-WinEvent -ea SilentlyContinue ` -ProviderName “Microsoft-Windows-Winlogon”| Where-Object { $_.TimeCreated -le [datetime]::today}
If ($ELogs)
{ Write-Host "Processing..."
ForEach ($Log in $ELogs)
{ If ($Log.Id -eq 7001)
{ $ET = "Logon"
}
ElseIf ($Log.Id -eq 7002)
{ $ET = "Logoff"
}
Else
{ Continue
}
$SID = $Log.Properties.Value.Value
$objSID = New-Object System.Security.Principal.SecurityIdentifier $SID
$objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
$Result += New-Object PSObject -Property #{
Time = $Log.TimeCreated
'Event Type' = $ET
User = $objUser.Value
}
}
$Result | Select Time,"Event Type",User | Sort Time -Descending | Out-GridView
Write-Host "Done."
}
Else
{ Write-Host "There was a problem reading the logs..."
}
}
get-logonhistory
Get-WinEvent and Get-EventLog use different arrays to store the details of an event log. Get-WinEvent users "Properties" and Get-EventLog Users "ReplacementStrings". By converting each to JSON your able to see the exact details of each, and locate the data your looking for. In this case it's the SID of the account that performed the event. Once this is obtain we can translate that SID into a Username and feed it into the results. Grid-View was opted for better usablity so you can filter the results if needed (i.e. wanting events for a specific user). Below is the final code;
function get-logonhistory{
cls
$Result = #()
Write-Host "Gathering Event Logs, this can take awhile..."
$ELogs = Get-WinEvent -ea SilentlyContinue ` -ProviderName “Microsoft-Windows-Winlogon”| Where-Object { $_.TimeCreated -le [datetime]::today}
If ($ELogs)
{ Write-Host "Processing..."
ForEach ($Log in $ELogs)
{ If ($Log.Id -eq 7001)
{ $ET = "Logon"
}
ElseIf ($Log.Id -eq 7002)
{ $ET = "Logoff"
}
Else
{ Continue
}
$SID = $Log.Properties.Value.Value
$objSID = New-Object System.Security.Principal.SecurityIdentifier $SID
$objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
$Result += New-Object PSObject -Property #{
'Date/Time' = $Log.TimeCreated
'Event Type' = $ET
User = $objUser.Value
}
}
$Result | Select "Date/Time","Event Type",User | Sort Time -Descending | Out-GridView -Title "System Logon Events"
Write-Host "Done."
}
Else
{ Write-Host "There was a problem reading the logs..."
}
}
get-logonhistory

Split-Pipeline parallel jobs script won't finish the last job

So I've been using Split-Pipeline module for some time now, and what bothers me about it, rarely does it really finish. I always get stuck with the last job in the queue hanging and have to either stop the script or kill ISE:
VERBOSE: Split-Pipeline: Jobs = 1; Load = End; Queue = 0
example: 300 servers, running a scan for hotfixes on them using split-pipeline. I'm using pretty standard parameters:
$servers | Split-Pipeline -Verbose -Count 10 { process {#some code for scanning#}}
So 10 jobs each loaded with 30 servers, first lets say 250 servers are scanned really fast, then it slows down a little and when the last job remains only, it never finishes...
Anyone experienced something similar? I've tested the same on several machines and it's always the same so I don't think it's related to the machine running the script.
EDIT: heres the code
$servers = (Get-Clipboard).trim() #server1..100
$KB = (Get-Clipboard).trim() -split ', ' #KB4022714, KB4022717, KB4022718, KB4022722, KB4022727, KB4022883, KB4022884, KB4022887, KB4024402, KB4025339, KB4025342, KB4021903, KB4021923, KB4022008, KB4022010, KB4022013, KB3217845
$servers | Split-Pipeline -Verbose -Variable KB -Count 10 { process {
$hash = [ordered]#{}
try
{
$hash.Hostname = $_
$os = gwmi win32_operatingsystem -ComputerName $_ -ErrorAction Stop
$hash.OS = $os.Caption
$hash.Architecture = $os.OSArchitecture
$today = [Management.ManagementDateTimeConverter]::ToDateTime($os.LocalDateTime)
$hash.LastReboot = [Management.ManagementDateTimeConverter]::ToDateTime($os.LastBootUpTime)
$hash.DaysSinceReboot = ($today - $hash.LastReboot).Days
}
catch
{
$hash.OS = $Error[0]
$hash.Architecture = 'N/A'
$hash.LastReboot = 'N/A'
$hash.DaysSinceReboot = 'N/A'
}
try
{
$hash.PendingReboot = icm -cn $_ {
if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -EA SilentlyContinue) { return $true }
if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -EA SilentlyContinue) { return $true }
if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -Ea SilentlyContinue) { return $true }
try
{
$util = [wmiclass]"\\.\root\ccm\clientsdk:CCM_ClientUtilities"
$status = $util.DetermineIfRebootPending()
if(($status -ne $null) -and $status.RebootPending)
{
return $true
}
}
catch{}
return $false
}
}
catch
{
$hash.PendingReboot = 'N/A'
}
try
{
$hotfix = Get-HotFix -ComputerName $_ -Id $KB -ErrorAction Stop
if ($hotfix)
{
$hash.Hotfix = $hotfix.HotFixID -join ','
}
else
{
$hash.Hotfix = "No Hotfix from the list applied"
}
}
catch
{
$hash.Hotfix = $Error[0]
}
$obj = New-Object -TypeName PSObject -Property $hash
$obj | Export-Csv c:\temp\hotfixes.csv -NoTypeInformation -Append -Force
}
}

Basic Output Redirection

I searched and put together a PowerShell script to check if a windows service (SNARE) is running or not in a list of servers. At the moment, the script prints "Snare is running" if it doesn't error and "Not installed/Powered off" if it meets an error. What I am also looking for is if the script doesn't end up in error, can I somehow take the output of Status (example below) and print "Snare is stopped"?
Status Name DisplayName
------ ---- -----------
Stopped SNARE SNARE
#Powershell
$serverList = gc Final.txt
$collection = $()
foreach ($server in $serverList) {
$status = #{
"ServerName" = $server
"TimeStamp" = (Get-Date -f s)
}
if (Get-Service -Name SNARE -ComputerName $server -EA SilentlyContinue) {
$status["Results"] = "Snare is running"
} else {
$status["Results"] = "Not installed/Powered off"
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
}
Assign the output from Get-Service to a variable, and grab the Status property from that:
if (($snare = Get-Service -Name SNARE -ComputerName $server -EA SilentlyContinue))
{
$status["Results"] = "Snare is running"
$status["Status"] = $snare.Status
}
else
{
$status["Results"] = "Not installed/Powered off"
$status["Status"] = "Unknown"
}