I have a piece of script to do some stuff on VMs. Here it is:
$powered = Get-VM VM-TEST4-* | Where-Object { $_.PowerState -eq 'PoweredOn'
ForEach-Object -InputObject $powered { Shutdown-VMGuest -VM $_ }
# taking snapshots etc etc
# BELOW FAILS
# start up machines in order
Where-Object -Like -Property Name -InputObject $powered -Value "VM-TEST4-DB*" | ForEach-Object { if ($_ -ne $null) { Start-VM -VM $_ } }
sleep -Seconds 180
Where-Object -Like -Property Name -InputObject $powered -Value "VM-TEST4-AUX*" | ForEach-Object { if ($_ -ne $null) { Start-VM -VM $_ } }
sleep -Seconds 180
Where-Object -Like -Property Name -InputObject $powered -Value "VM-TEST4-WEB*" | ForEach-Object { if ($_ -ne $null) { Start-VM -VM $_ } }
sleep -Seconds 180
My problem with this code is that nothing gets started and I only wait. Now the idea behind it was to filter out correct server type so that DB starts earlier than AUX and WEB while $_ -ne $null check protects me if Where-Object returns no results (we have an enviroment with no AUX servers).
Any idea how to make it work properly?
The simplified where-object syntax (including the -like switch parameter) was introduced in 3.0. The -like operator works fine in the standard where-object syntax. You want something like this:
Where-Object {$_.Name -like "VM-TEST4-WEB*"}
EDIT:
The -Like parameter for where-object (which looks like an operator in the simplified syntax) was added in 3.0. The -Like operator which does string comparisons has been in PowerShell since 1.0
Related
I have a folder containing text-files with a standardized naming-scheme like:
2021-03-16_21-25-55_Client1_Edward.Hall_ServerResponse.json
2021-03-16_21-25-33_Client2_Eloise.Glover_ServerResponse.json
2021-03-16_21-17-38_Client3_Millie.Walsh_ServerResponse.json
2021-03-16_21-17-30_Client4_Lilly.Morton_ServerResponse.json
2021-03-16_21-15-45_Client5_Tia.Curtis_ServerResponse.json
2021-03-16_21-15-23_Client1_Edward.Hall_ServerResponse.json
2021-03-16_21-15-10_Client1_Lilly.Morton_ServerResponse.json
2021-03-16_21-15-03_Client2_Eloise.Glover_ServerResponse.json
2021-03-16_21-12-14_Client2_Eloise.Glover_ServerResponse.json
2021-03-16_21-11-25_Client3_Administrator_ServerResponse.json
I want to filter the files and retrieve the latest file (LastWriteTime) of a specific Computername-/Username-combination. Therefore I want to use a code like this:
# $env:COMPUTERNAME = "Client1"
# $env:USERNAME = "Edward.Hall"
$MyFolder = "C:\MyFolder"
Get-ChildItem -Path $MyFolder -File -ErrorAction SilentlyContinue | Where-Object {
$_.Extension -eq ".json" -and $_.COMPUTERNAME -eq $env:COMPUTERNAME -and $_.USERNAME -eq $env:USERNAME
} | Sort-Object -Descending -Property LastWriteTime | Select-Object -First 1
Of course the part -and $_.COMPUTERNAME -eq $env:COMPUTERNAME -and $_.USERNAME -eq $env:USERNAME is NOT working and should only show up the direction to what I imagine.
In the example above the result should be the file "2021-03-16_21-25-55_Client1_Edward.Hall_ServerResponse.json".
I was thinking of using -match, but it should be a exact match -eq.
Could you please help me to find a solution for this?
Thank you very much!
As long as you can count on the name format always conforming to that standard you can just split up the name strings for your required sections:
# $env:COMPUTERNAME = "Client1"
# $env:USERNAME = "Edward.Hall"
$MyFolder = "C:\MyFolder"
Get-ChildItem -Path $MyFolder -File -ErrorAction SilentlyContinue | Where-Object {
($_.Extension -eq ".json") -and ($_.Name.Split('_')[2] -eq $env:COMPUTERNAME) -and ($_.Name.Split('_')[3] -match $env:USERNAME)
} | Sort-Object -Descending -Property LastWriteTime | Select-Object -First 1
I am trying to stop multiple services running with specific name.
I would like to have a PowerShell script i can execute and let stop all services exclude services I specify in a text file.
help please.
$ServiceRunName = Get-Service | where {($_.Name -like "ser_*") -and ($_.Status -eq "Running")} | Select-Object -Property Name
$fileServices = Get-Content ..\run.txt
If ($fileServices -ne $null) {`enter code here`
foreach ($line in $fileServices ) {
$service = $line
"$service"
"$name"
if($service -eq $ServiceRunName ){
" not kill "
}else {
"kill"
}
}
}
Below code defines an array, including the names of the services NOT to stop, instead of reading a file:
# Alternative to array: $servicesNotToStop = Get-Content ..\run.txt
$servicesNotToStop = #("WlanSvc","vpnagent")
Get-Service | ? {$_.Status -eq "Running"} | ? { $servicesNotToStop -notcontains $_.Name} | % { Stop-Service $_ -WhatIf}
Get-Service | ? {$_.Status -eq "Running"} ... Get all services in state running and send them to the pipeline.
? { $servicesNotToStop -notcontains $_.Name} ... check if the actual object ($_) in the pipieline (type=ServiceController) is NOT included in the array (the name property includes the service name).
% { Stop-Service $_ -WhatIf} ... perform stop service foreach object that was fowarded by beforehand ?-filter. The -WhatIf-switch of Stop-Service prevents stopping the serivce, BUT it prints what service would be stopped. A lot of PowerShell cmdlets offer WhatIf-switches to test the command in a "dry"-run.
From msdn:
-WhatIf
Shows what would happen if the cmdlet runs. The cmdlet is not run.
UPDATE 1
Read services that shouldn't be stopped from file:
$servicesToStop = Get-Content .\dontStop.txt
Get-Service | ? { $_.Status -eq "Running" } | ? { $servicesToStop -notcontains $_.Name } | % { Stop-Service $_ -WhatIf }
Format of dontStop.txt:
service1
service2
UPDATE 2
More or less the code of you question:
$ServiceRunName = Get-Service | where-object { $_.Status -eq "Running" } | Select-Object -ExpandProperty Name
$fileServices = Get-Content "..\run.txt"
If ($null -ne $fileServices) {
foreach ($service in $ServiceRunName) {
foreach ($serviceNotToStop in $fileServices ) {
if ($service -ne $serviceNotToStop ) {
Stop-Service $service -WhatIf
break
}
}
}
}
Hope that helps
Using PowerCLI to filter a list of virtual machines:
Get-VM | Where-Object {$_.Name -ne 'VM1001' -and $_.Name -ne 'VM2002' -and $_.Name -ne 'VM3003' -and $_.Name -ne 'VM4004'} | Select_Object ...
Is there a cleaner/better way to filter results? This would improve script readability.
Thanks
Like #JosefZ commented, using the -notin is good for PowerShell version 3 and later:
Get-VM | Where-Object { $_.Name -notin #('VM1001','VM2002','VM3003','VM4004') }
On PowerShell version 2, you can still can use the -notcontains comparison operator:
$Excluded = #('VM1001','VM2002','VM3003','VM4004')
Get-VM | Where-Object { $Excluded -notcontains $_.Name }
I have a script containing a function Create-RebootData containing child functions such as Full and Full has a child function named Generate-RebootData where the output variable $Global:result is created.
Within Full there are multiple Where-Object two statements to filter the $Global:result into by date and time. Example below.
Is there an easier method to accomplish this instead of the multiple Where-Object statements?
The desired result are
Set-StrictMode -Version 1.0
Function Create-RebootData{
[CmdletBinding(SupportsShouldProcess=$true,DefaultParameterSetName="ViewOnly")]
Param(
[Parameter(ParameterSetName="ViewOnly")]
[Switch]$ViewOnly,
[Parameter(ParameterSetName="Full")]
[Switch]$Full,
)
Switch ($PSCmdlet.ParameterSetName){
"ViewOnly"
{
ViewOnly
}
"Full"
{
Full
}
}#end switch
Function Full{
Generate-RebootData
$Global:result | Where-Object {$_ -like '*fri?2:00*' -and $_.MaintenanceWindow `
-notmatch 'all.da.servers' -and $_.Server -match "^ITD"} | % {"{0}" -f $_.Server} | `
Out-File D:\Scripts\Full-Servers.txt -Append
$Global:result | Where-Object {$_ -like '*fri?2:00*' -and $_.MaintenanceWindow `
-notmatch 'all.da.servers' -and $_.Server -match "^ITD"} | `
% {"{0}" -f $_.MaintenanceWindow -replace `
"^NA.+", "$((get-date).AddDays(1).ToString('MM-dd-yy')) 01:50"} | `
Out-File D:\Scripts\Full-Times.txt -Append
}
Function Generate-RebootData{
IF(Get-Command Get-SCOMAlert -ErrorAction SilentlyContinue){}ELSE{Import-Module OperationsManager}
"Get Pend reboot servers from prod"
New-SCOMManagementGroupConnection -ComputerName Server01
$AlertData = Get-SCOMAlert -Criteria "MyString" | Select NetbiosComputerName
New-SCOMManagementGroupConnection -ComputerName Server02
$AlertData += Get-SCOMAlert -Criteria "MyString" | Select NetbiosComputerName
"Remove duplicates"
$AlertDataNoDupe = $AlertData | Sort NetbiosComputerName -Unique
"Create hash table"
$table = #{}
"Populate hash table"
$MaintenanceWindow = Import-Csv D:\Scripts\MaintenanceWindow2.csv
$MaintenanceWindow | ForEach-Object {$table[$_.Computername] = $_.'Collection Name'}
"Create final object"
$Global:result = #{}
"Begin Loop"
$Global:result = $AlertDataNoDupe | ForEach-Object { [PSCustomObject] #{
Server=$_.NetbiosComputerName
MaintenanceWindow= if($table.ContainsKey($_.NetbiosComputerName)){
$table[$_.NetbiosComputerName]
}Else { "Not Found!"}
PingCheck=IF(Test-Connection -Count 1 $_.NetbiosComputerName -Quiet -ErrorAction SilentlyContinue){"Alive"}
ELSE{"Dead"}
LastReboot=Try{$operatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $_.NetbiosComputerName -ErrorAction Stop
[Management.ManagementDateTimeConverter]::ToDateTime($operatingSystem.LastBootUpTime)}
Catch{"Access Denied!"}
} }
}
You can do it like this:
$Global:result | Where-Object {
$_ -like '*fri?2:00*' -and
$_.MaintenanceWindow -notmatch 'all.da.servers' -and
$_.Server -match '^ITD'
} | ForEach-Object {
'{0}' -f $_.Server | Out-File D:\Scripts\Full-Servers.txt -Append
'{0}' -f $_.MaintenanceWindow -replace '^NA.+',
'{0} 01:50' -f (Get-Date).AddDays(1).ToString('MM-dd-yy') | Out-File D:\Scripts\Full-Times.txt -Append
}
But I agree with Mathias' comment, this function probably should be refactored.
Every time I run the script below I get
Cannot bind argument to parameter 'FilePath' because it is null.
It was working last night. No changes have been made and this morning it just fails. the funny thing is if i save the script and then run it, it works. However if I clear console then run it again it fails.
Am I missing something obvious?
New-Item -ItemType Directory -Force -Path C:\NonStandard_Services
set-location C:\NonStandard_Services
$Computers= Get-Content C:\computers.txt
$Report= $file
$file= $Computer
ForEach ($Computer in $Computers)
{
Get-WmiObject -ComputerName $Computer -class Win32_Service -ErrorAction SilentlyContinue |
Where-Object -FilterScript {$_.StartName -ne "LocalSystem"}|
Where-Object -FilterScript {$_.StartName -ne "NT AUTHORITY\NetworkService"} |
Where-Object -FilterScript {$_.StartName -ne "NT AUTHORITY\LocalService"} |
Select-Object -Property StartName,Name,DisplayName|
ConvertTo-Html -Property StartName,Name,DisplayName -head $HTML -body "<H2>Non- Standard Service Accounts on $Computer</H2>"| Out-File $Report -Append}
#Rename-Item c:\GP_Services\Report.htm $file
Get-ChildItem | Where-Object {$_.extension -ne ".htm"} | Rename-Item -newname { $_.name + '.htm' }
$Report= $file
$file= $Computer
ForEach ($Computer in $Computers)
{
...
}
You assign variables to other variables before they were assigned a value themselves. Change the above to this:
ForEach ($Computer in $Computers) {
$file = $Computer
$Report = $file
...
}
Or directly use $Computer in Out-File:
... | Out-File "$Computer.txt" -Append