Powershell Shutdown VM Script - powershell

I have a script that can shutdown vms based on names entered into an array.
That part works fine, but the next part after the shutdown initiates is supposed to wait for a period and notify how many vms it is waiting to shut down before it moves on to another phase of shutting down other vms.
I am not getting any vms counted out of the array. Here is the code for a particular phase:
$waittime = 5 #Seconds
#Create Phase 1 Array
[Array] $PHASE1 = "TestSvr2008"
# For each of the VMs on the Hyperv hosts that are powered on
Foreach ($VM in ($PHASE1 | %{ Get-VM | Where { $_.State -eq "Running" }})){
# Shutdown the guest cleanly
$VM | Stop-VM -Confirm:$false }
# Set the amount of time to wait before assuming the remaining powered on guests are stuck
$waittime = 120 #Seconds
$Time = (Get-Date).TimeofDay
do {
# Wait for the VMs to be Shutdown cleanly
sleep 1.0
$timeleft = $waittime - ($Newtime.seconds)
$numvms = ($PHASE1 | %{ Get-VM | Where { $_.$VM -eq $PHASE1 }}).Count
Write "Waiting for shutdown of $numvms VMs or until $timeleft seconds"
$Newtime = (Get-Date).TimeofDay - $Time
} until ((#($PHASE1 | %{ Get-VM | Where { $_.$VM -eq $PHASE1 }}).Count) -eq 0 -or ($Newtime).Seconds -ge $waittime)
Thanks

This line in do and until blocks has issues:
$numvms = ($PHASE1 | %{ Get-VM | Where { $_.$VM -eq $PHASE1 }}).Count
$_.$VM is typo, it should be $_.VM
$_.VM -eq $PHASE1 tries to check if VM name is equal to array. Comparisons do not work that way.
ForeEach-Object is unnecessary
Get-Vm accepts array of VM names by pipeline or directly as Name parameter. So you can do this:
$numvms = ($PHASE1 | Get-VM).Count
or
$numvms = (Get-VM -Name $PHASE1).Count

Related

Using Get-Job to test-connection, but quite different [duplicate]

This question already has answers here:
If using Test-Connection on multiple computers with -Quiet how do I know which result is for which computer?
(2 answers)
Closed 2 years ago.
It's my first post here, I'm tring to write scripts on PS on my own, now my target is to write script that checks if computer is online at network, for example: test-Connection 192.168.0.1, 2, 3 etc. Doing this one by one on loop for takes some time if some computers are offline, I've found some tutorials on this site to use -AsJob param, but I'm not really Sure how could it work. I mean I'd like to output every checked PC to excel, so i need if operator. eg:
if (Job1 completed successfull (computer pings)){
do smth}...
I need to get output from Job boolean (true/false), but one by one. I'm taking my first steps in PS, I've made program that checks it one by one in for loop, but as i said it take some time till my excel file fill...
I can see, that AsJob makes working more effective and I think it's important to understand it
Thanks and sorry for bad text formatting, by the time I'll go on with this!
In your example, in the Start-Job scriptblock you are trying to access $_ which is not available in the codeblock scope. If you replace $_ with $args[0] it should work since you are passing in the $ip value as an argument
Your Example
$ipki = Get-Content 'C:\Users\pchor\Desktop\ipki.txt'
foreach ($ip in $ipki) {
Start-Job -Name "$ip" -ScriptBlock {
Test-Connection $_ -Count 1 # <---- replace $_ with $args[0]
} -ArgumentList $_ # <----- change $_ to $ip
}
You'll probably also want to wait for all the jobs to finish. I recommend something like this
$computers = #(
'www.google.com'
'www.yahoo.com'
)
$jobs = $computers |
ForEach-Object {
Start-Job -ScriptBlock {
[pscustomobject]#{
Computer = $using:_
Alive = Test-Connection $using:_ -Count 1 -Quiet
}
}
}
# Loop until all jobs have stopped running
While ($jobs |
Where-Object { $_.state -eq 'Running' }) {
"# of jobs still running $( ($jobs | Where-Object {$_.state -eq 'Running'}).Count )";
Start-Sleep -Seconds 2
}
$results = $jobs | Receive-Job | Select-Object Computer, Alive
$results | Format-Table
Output
Computer Alive
-------- -----
www.google.com True
www.yahoo.com True
To modify the properties to what you want there are different ways of doing this. Easiest in this case is probably to use a calculated property
$newResults = $results |
Select-Object Computer,
#{Label = 'State'; Expression = { if ($_.Alive) { 'Online' } else { 'Offline' } } }
Objects will now look like this (I added another fake address to illustrate offline state)
Computer State
-------- -----
www.google.com Online
www.yahoo.com Online
xxx.NotAValidAddress.xxx Offline
You can then export the objects to csv using Export-csv
$newResults | Export-Csv -Path c:\temp\output.csv

User input VM name should start via powerhsell

Purpose - I am trying to start vm if it is in stop state
Note - user will input Vm name , if its stop then it will start otherwise it will pop up that server already in started state.
$user = 'tooltest' #Vmname
$rg = Get-AzureRmResourceGroup
$data= $rg.ResourceGroupName
foreach ( $d in $data){
$res = Get-AzureRmResource | Where-Object {$_.ResourceGroupName -eq $d}
if ( $res.Name -eq $user){
Write-Output $res.Name
Write-Output $res.ResourceGroupName
$gg = Get-AzureRmVM -ResourceGroupName [string]$res.ResourceGroupName -Name $user -Status
If i m trying to print $res.ResourceGroupName - i am getting output of resourcegroup name 26 times (26 resources i have in That RG)
I wanted to print RG only one time , can anybody help me on that
One way to filter your output is just to select how many objects do you want with the Select-Object cmdlet.
Write-Output $res.ResourceGroupName | Select -First 1

Script to return the name of VMs in defined groups that appear on the same VMHost

What I am trying to achieve
I am writing a script that will run every hour and will send an email if certain VMs have been found running on the same vHost. I don't really care where each VM runs as long as one of its other VMs are not on the same vHost. The groupings are currently 4 VMs but I want it to work with N VMs in a group.
Firstly I define my groups of VMs like so:
$group1 = #("VM01", "VM02", "VM03", "VM04")
$group2 = #("SERVERA", "SERVER2")
$group3 = #("WIN01", "WIN02", "WIN03")
$groups = #($group1, $group2, $group3)
I can then do a ForEach on $groups and walk through each group in turn retrieving the name of the VMHost the VM is on:
ForEach($group in $groups) {
$vmhosts = New-Object 'string[]' $group.Length
For($i=0; $i -le ($group.Length -1); $i++) {
$vmhosts[$i] = Get-VM -Name $group[$i] | Get-VMHost
}
}
This gives me the IP/hostname of the VMhost into the array $vmhosts with position matching the index of the VM in its group array.
This is where I am struggling to figure out a way to determine if any VMs are on the same VMHost and report that in my email with text like the following for each VM on the same VMHost within a group (but reports on all groups):
VM01 is on the same VMHostas VM02
VM01 is on the same VMHostas VM03
VM02 is on the same VMHostas VM03
WIN02 is on the same VMHost as WIN03
If no group of VMs are on the same VMHosts then it simply returns, "All VMs are separated correctly."
What I have tried so far
I tried to retrieve the unique VMHost from $vmhosts with:
$unique = $vmhosts | Select -Unique
But then I need to know when VMs it was on that VMHost returned. So I tried to locate it in the $vmhosts which worked with 3 VMs but when expanded to 4 it fails to return anything.
I'm pretty sure this could be done much better....
How about
$vmGrp1 = #("VM01", "VM02", "VM03", "VM04")
$vmGrp2 = #("SERVERA", "SERVER2")
$vmGrp3 = #("WIN01", "WIN02", "WIN03")
$vmGrps = #($vmGrp1, $vmGrp2, $vmGrp3)
$finds = #()
foreach ($vmGrp in $vmGrps)
{
$vms = get-vm $vmGrp
$HostGrps = $vms | Group-Object #{e={$_.vmhost.name}}
$finds += $HostGrps | % {if ($_.Count -gt 1) {$_.Name + " has " + ( $_.Group | Select -ExpandProperty Name) }} # this breaks in PS v2: + $_.Group.Name}}
}
if ($finds) {$finds} else {"All VMs are separated correctly."}

Input and Output of foreach into columns in table

I currently want to check if a list of processes are running, then display the result within a table such as:
Process Status
======= ======
Process 1 Running
Process 2 Not Running
Process 3 Running
I have the below code which produces an output showing each input and output as a string, but it looks messy depending on the length of the Process name.
$Node = Read-Host -Prompt 'Input Node name'
$Process = #("Process1", "Process2", "Process3")
$Process | foreach-object {if(!(Get-Process -Name $_ -ComputerName $Node - ErrorAction SilentlyContinue)) {"$_ - Not Running"} else {"$_ - Running"}}
I am at a loss. All help appreciated.
Better (faster) to make a single remoting call to get all the processes, than one per process, so do that and store all the results - at least the names of the processes.
The next part is non-trivial. The way PowerShell and the neatly formatted tables work, is by making one object (bundle of things all together) for each table row, with each object having properties for each column name.
# Processes to look for
$Process = #("Process1", "Process2", "Process3")
$Node = Read-Host -Prompt 'Input Node name'
# Get running processes, and only keep their names
$runningProcesses = Get-Process -ComputerName $Node -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty Name
$Process | ForEach-Object {
# For each process name to look for, generate a hashtable of
# columns and their values,
# then cast it into a PS Object
[PSCustomObject]#{
'ProcessName' = $_
'Status' = if ($runningProcesses -contains $_) { "Running" } else { "Not Running" }
}
}
This gives a neat formatted table output, and is also structured data so you can feed the output of this into | ForEach-Object { $_.Status } and pick out the individual parts by name, something you can't do as neatly with your string formatted approach.
Try this:
$node = Read-Host -Prompt 'Input Node name'
$processList = "Process1", "Process2", "Process3"
$processList |
ForEach-Object {
[PsCustomObject]#{
NodeName = $node
ProcessName = $_
IsRunning = (Get-Process -Name $_ -ComputerName $node -ErrorAction SilentlyContinue | Select-Object -First 1) -ne $null
}
}
Output will be like this:
NodeName ProcessName IsRunning
-------- ----------- ---------
Node1 Process1 True
Node1 Process2 True
Node1 Process3 False

Get CPU % of a particular process using powershell

I want to get the CPU usage % (not processor time) of a particular process using a powershell command.
Example: (Windows 8 Task Manager)
I want to get that 2.9% with a command.
Here is the correct answer which is support case then you have multiple processs with same name https://stackoverflow.com/a/34844682/483997
# To get the PID of the process (this will give you the first occurrance if multiple matches)
$proc_pid = (get-process "slack").Id[0]
# To match the CPU usage to for example Process Explorer you need to divide by the number of cores
$cpu_cores = (Get-WMIObject Win32_ComputerSystem).NumberOfLogicalProcessors
# This is to find the exact counter path, as you might have multiple processes with the same name
$proc_path = ((Get-Counter "\Process(*)\ID Process").CounterSamples | ? {$_.RawValue -eq $proc_pid}).Path
# We now get the CPU percentage
$prod_percentage_cpu = [Math]::Round(((Get-Counter ($proc_path -replace "\\id process$","\% Processor Time")).CounterSamples.CookedValue) / $cpu_cores)
Get-Process -Name system | select CPU
Get the cpu time at 2 instance as (cpu2-cpu1)/(t2-t1)*100. You will get CPU value in %.
Get-Process -Name system | select CPU # Get the cpu time at 2 instance as (cpu2-cpu1)/(t2-t1)*100. You will get CPU value in %.
$processName = 'OUTLOOK'
$sleep_time = 1 # value in seconds
while (1) {
$CPU_t1 = Get-Process -Name $processName | Select CPU
$CPU_t1_sec = $($CPU_t1.CPU)
#Write-Host "CPU_t1: $($CPU_t1.CPU)"
$date1 = (Get-Date)
sleep -Seconds $sleep_time
$CPU_t2=Get-Process -Name $processName | Select CPU
#Write-Host "CPU_t2: $($CPU_t2.CPU)"
$CPU_t2_sec = $($CPU_t2.CPU)
$date2 = (Get-Date)
$date_diff = $date2 - $date1
$diff_time = $date_diff.seconds
#Write-Host "TimeDiff: $diff_time"
#compute them to get the percentage
$CPU_Utilization = ($CPU_t2_sec - $CPU_t1_sec)/$diff_time
$CPU_Utilization_per = $CPU_Utilization * 100
#Sleep $sleep_time
Clear-Host Write-Host "CPU_Utilization_Per: $CPU_Utilization_per"
#Write-Host "====================="
}
Get-Process -Name PowerShell | Select CPU
Is this what you're looking for?
Or something more monitoring based?
param
(
[String]
[Parameter(Mandatory)]
$Title
)
do
{
$process = Get-Process -Name $Title
$process
Start-Sleep -Seconds 1
} while ($process)