The below code is designed to check two hard-coded service names (these will never change) and if they are not found to then state the services are not present. It does this but it outputs two lines as there are two services to check.
Is there a way to alter this so that only one output is given after checking both services?
$servicenames = ("Service 1", "Service 2")
Write-Host 'Checking for Services'
function servicevalidation {
foreach ($sn in $servicenames) {
if ((Get-Service "$servicenames*" -Include $servicenames) -eq $null) {
Write-Host 'Young jedi you are mistaken, this server is not a collector!'
} else {
Write-Host "$servicenames present"
}
}
}
Get-Service accepts a list of services, so you can simply do the following without a loop. I would recommend passing the services as an argument, though, if you're encapsulating the check in a function anyway.
function servicevalidation($svc) {
if (Get-Service $svc -ErrorAction SilentlyContinue) {
Write-Host 'Young jedi you are mistaken, this server is not a collector!'
} else {
Write-Host "$svc present."
}
}
servicevalidation $servicenames
The -ErrorAction SilentlyContinue suppresses error output in case a service is not present.
If you want to know which service exactly is missing you could do so with a Where-Object filter:
function servicevalidation($svc) {
$missing = $svc | Where-Object {
-not (Get-Service $_ -ErrorAction SilentlyContinue)
}
if ($missing) {
Write-Host "missing service: $missing"
} else {
Write-Host "$svc present."
}
}
Related
I have a PowerShell function which reads the status of my SQL agent job.
function get-SQLJobStatus
{
param(
[string]$server,
[string]$jobName
)
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$srv = New-Object Microsoft.SqlServer.Management.SMO.Server($server)
if($jobName)
{
$srv.JobServer.Jobs | where {$_.Name -match $jobName} | Select CurrentRunStatus
}
}
If I run this function, the return looks like below
CurrentRunStatus
________________
Idle
What I want to do is run this get-SQLJobStatus function and check if CurrentRunStatus == Idle. It seems like I can't just store those return into the variable so I am stuck.
Any idea?
Since your function returns an object with the CurrentRunStatus property, you can check it by using member access (.propertyName) from the returned function call:
if ((get-SQLJobStatus -server 'servername' -jobname 'jobname').CurrentRunStatus -eq 'Idle') {
# the job is idle. run code here
}
else {
# the job is not idle. run code here
}
If you want to store the output of the function call, you can still run a similar check as above:
$status = get-SQLJobStatus -server 'servername' -jobname 'jobname'
if ($status.CurrentRunStatus -eq 'Idle') {
# the job is idle. run code here
}
else {
# the job is not idle. run code here
}
As an aside, your $_.Name -match $jobName comparison can return multiple objects. This is because -match uses regex and your $jobname value is likely unanchored. To return an exact match, you could opt for $_.Name -eq $jobName.
I have an array of servers that I need to loop through, and have an attribute assigned only to specific servers (this happens later in the script).
Here's the array:
$test = #('test_dc','test_fp','test_ts','test_ap')
In the array, I have a domain controller, file/print, terminal server, and application servers (in that order).
The only servers that should get the attribute are the fp, ts, and ap servers.
Here's what I've tried thus far:
foreach ($item in $test) {
Write-Host $item "`n"
Write-Host "Start IF here `n"
if ($item.Name -like '*fp*') {
Write-Host "Found $item"
} else {
Write-Host "ELSE `n"
Write-Host '-=-=-=-=-'
}
}
Here's the output from that:
PS C:\Users\me\Desktop> .\scratch.ps1
test_dc
Start IF here
ELSE
-=-=-=-=-
test_fp
Start IF here
ELSE
-=-=-=-=-
test_ts
Start IF here
ELSE
-=-=-=-=-
test_ap
Start IF here
ELSE
-=-=-=-=-
PS C:\Users\me\Desktop>
According to the way I think it should work, I should see this:
...
test_fp
Found test_fp
...
I've also tried this:
if ($test -contains '*fp') {
Write-Host "Found $_"
} else {
Write-Host 'end'
}
and I just get an empty line.
You're seeing extra info being written to host as you have indefinite writes for each item, regardless if it matches. Since you're including the else statement as well, you're going to see extra stuff written for the non-matched items. Your foreach loop also specifies the name attribute for the object, while your $test array only contains strings.
Here's what I updated it to to limit to only write the host name in your loop if it matches *fp*, otherwise writing your divider:
$test = #('test_dc','test_fp','test_ts','test_ap')
foreach ($item in $test) {
if ($item -like '*fp*') {
Write-Host "Found $item"
} else {
Write-Host '-=-=-=-=-'
}
}
Running that will output this:
-=-=-=-=-
Found test_fp
-=-=-=-=-
-=-=-=-=-
So my larger project is to come up with a way of finding UNC path names that are too long, and once I have that information, use them as mapped drives so that I can run Get-Child information below that point.
I have the following code which gets me a Write of the folders that match that criteria, and display the UNC path to me, and I would like to add that information to an array that can be called back
Here is the code I have at the moment:
Get-ChildItem "\\Server\Share" -recurse -ErrorAction SilentlyContinue -ErrorVariable err
foreach ($errorRecord in $err)
{
if ($errorRecord.Exception -is [System.IO.PathTooLongException])
{
Write-Warning ($errorRecord.TargetObject)
$ErrorArray = $errorRecord
}
else
{
Write-Error -ErrorRecord $errorRecord
}
}
Out-File C:\ErrorArray.txt
Being new to PS, can anyone please point me in the right direction please?
Add a variable $results to hold errors you want to capture and pipe that out to a file. As well as that grab the string with error message - $errorRecord.TargetObject - rather than the entire error object.
$results = #()
Get-ChildItem "\\Server\Share" -recurse -ErrorAction SilentlyContinue -ErrorVariable err
foreach ($errorRecord in $err)
{
if ($errorRecord.Exception -is [System.IO.PathTooLongException])
{
Write-Warning ($errorRecord.TargetObject)
$results += ,$errorRecord.TargetObject
}
else
{
Write-Error -ErrorRecord $errorRecord
}
}
$results | Out-File C:\temp\ErrorArray.txt
I know there is a quicker way of doing this but I just don't know how to approach it. I also need it to work in PowerShell V2 as I still have Windows 2003 servers I need to handle with this.
Basically this would stop services and then check to see if those services were stopped. I have a location to define them earlier in the script. I am using this to deploy code to servers so depending on the code from Development I may need to stop 1 to 4 services and looking to see if there is a way that I can define the services and not have to comment out code below if I only use 2 services as opposed to four.
#Turn off Services
stop-service $Service1 -force
# stop-service $Service2 -force
# stop-service $Service3 -force
# stop-service $Service4 -force
$VerifyServiceStopped1 = Get-Service $Service1 | Where-Object {$_.status -eq "Stopped"} | select -last 1
# $VerifyServiceStopped2 = Get-Service $Service2 | Where-Object {$_.status -eq "Stopped"} | select -last 1
# $VerifyServiceStopped3 = Get-Service $Service3 | Where-Object {$_.status -eq "Stopped"} | select -last 1
# $VerifyServiceStopped4 = Get-Service $Service4 | Where-Object {$_.status -eq "Stopped"} | select -last 1
if ($VerifyServiceStopped1) {Write-Host $Service1' Stop = Pass (0)'} else {Write-Host $Service1' Stop = Fail (1000)'; Exit '1000'}
# if ($VerifyServiceStopped2) {Write-Host $Service2' Stop = Pass (0)'} else {Write-Host $Service2' Stop = Fail (1001)'; Exit '1001'}
# if ($VerifyServiceStopped3) {Write-Host $Service3' Stop = Pass (0)'} else {Write-Host $Service3' Stop = Fail (1002)'; Exit '1002'}
# if ($VerifyServiceStopped4) {Write-Host $Service4' Stop = Pass (0)'} else {Write-Host $Service4' Stop = Fail (1003)'; Exit '1003'}
Any suggestions?
Thanks
Dwight
Something like this, perhaps?
$services = #(
'Service1',
'Service2',
'Service3',
'Service4')
Get-service |
Where { $Services -Contains $_.Name} |
Foreach {
#Stop Service
#Verify Service
#Restart Service
}
I'm not sure how you want to handle this, but since arrays is tagged:
$services = #("ServiceName1","ServiceName2")
#Turn off Services
foreach($service in $services) {
stop-service $Service1 -force
$VerifyServiceStopped1 = Get-Service $Service1 | Where-Object {$_.status -eq "Stopped"} | select -last 1
if ($VerifyServiceStopped1) {
Write-Host $Service1' Stop = Pass (0)'
} else {
Write-Host $Service1' Stop = Fail (1000)'
Exit '1000'
}
}
"so depending on the code from Development I may need to stop 1 to 4 services"
If you cannot define the logic involved in that decision, how do you plan to automate it?
Just wanted to provide the entire code used in case someone else is looking for something similar - thanks to everyone for throwing ideas into the mix it was very helpful.
#Define Services
$Service1 = 'servicename'
$Service2 =
$Service3 =
$Service4 =
$Service5 =
$Service6 =
$Service7 =
$Service8 =
$Service9 =
$services = #(
$Service1,
$service2,
$service3,
$service4,
$Service5,
$Service6,
$Service7,
$Service8,
$Service9)
#Stop Services
Get-service |
Where { $Services -Contains $_.Name} |
Foreach {
$_ | stop-service
}
Set-Service |
Where { $Services -Contains $_.Name} |
Foreach {
$_ | -startuptype "Disabled"
}
#Verify Services
Get-service |
Where { $Services -Contains $_.Name} |
Foreach {
if ((Get-Service $_.Name).Status -eq "stopped") {Write-Host 'Service Start Pass (0)'} else {Write-Host 'Start Fail (1000)';Exit '1000'}
}
#Start Services
Set-Service |
Where { $Services -Contains $_.Name} |
Foreach {
$_ | -startuptype "Automatic"
}
Get-service |
Where { $Services -Contains $_.Name} |
Foreach {
$_ | start-service
}
#Verify Services
Get-service |
Where { $Services -Contains $_.Name} |
Foreach {
if ((Get-Service $_.Name).Status -eq "running") {Write-Host 'Service Start Pass (0)'} else {Write-Host 'Start Fail (2000)';Exit '2000'}
}
This allows me to have someone list any services they need to stop / start to deploy custom code out there - also then I will be moving this to stop executables in order to replace them and other files for verification. Sadly we are handed a collection of exe's and dll's to just hot swap for some deployments of software so this is why this was needed. I wanted a way of defining what I need to move in and out per deployment but didn't want to comment out lines throughout the script that I didn't need (i.e. only had two services so needed to comment out the others throughout the script).
Just access the status directly:
> (Get-Service -Name "Windows Time").Status -eq "Stopped"
$true
or
if ((Get-Service -Name "Windows Time").Status -eq "Stopped")
{
Write-Host "Yep..."
}
or if you want to be really terse, you can use the alias gsv:
> (gsv "Windows Time").Status -eq "Stopped"
$true
You could also make a function:
Function IsStopped ($name)
{
((Get-Service $name).Status -eq "Stopped")
}
if (IsStopped "Windows Time")
{
Write-Host "foo"
}
I'm working on a PowerShell script that finds all the files with PATTERN within a given DIRECTORY, prints out the relevant lines of the document with the PATTERN highlighted, and then replaces the PATTERN with a provided REPLACE word, then saves the file back. So it actually edits the file.
Except I can't get it to alter the file, because Windows complains about the file already being open. I tried several methods to solve this, but keep running into the issue. Perhaps someone can help:
param(
[string] $pattern = ""
,[string] $replace = ""
,[string] $directory ="."
,[switch] $recurse = $false
,[switch] $caseSensitive = $false)
if($pattern -eq $null -or $pattern -eq "")
{
Write-Error "Please provide a search pattern." ; return
}
if($directory -eq $null -or $directory -eq "")
{
Write-Error "Please provide a directory." ; return
}
if($replace -eq $null -or $replace -eq "")
{
Write-Error "Please provide a string to replace." ; return
}
$regexPattern = $pattern
if($caseSensitive -eq $false) { $regexPattern = "(?i)$regexPattern" }
$regex = New-Object System.Text.RegularExpressions.Regex $regexPattern
function Write-HostAndHighlightPattern([string] $inputText)
{
$index = 0
$length = $inputText.Length
while($index -lt $length)
{
$match = $regex.Match($inputText, $index)
if($match.Success -and $match.Length -gt 0)
{
Write-Host $inputText.SubString($index, $match.Index) -nonewline
Write-Host $match.Value.ToString() -ForegroundColor Red -nonewline
$index = $match.Index + $match.Length
}
else
{
Write-Host $inputText.SubString($index) -nonewline
$index = $inputText.Length
}
}
}
Get-ChildItem $directory -recurse:$recurse |
Select-String -caseSensitive:$caseSensitive -pattern:$pattern |
foreach {
$file = ($directory + $_.FileName)
Write-Host "$($_.FileName)($($_.LineNumber)): " -nonewline
Write-HostAndHighlightPattern $_.Line
%{ Set-Content $file ((Get-Content $file) -replace ([Regex]::Escape("[$pattern]")),"[$replace]")}
Write-Host "`n"
Write-Host "Processed: $($file)"
}
The issue is located within the final block of code, right at the Get-ChildItem call. Of course, some of the code in that block is now a bit mangled due to me trying to fix the problem then stopping, but keep in mind the intent of that part of the script. I want to get the content, replace the words, then save the altered text back to the file I got it from.
Any help at all would be greatly appreciated.
Removed my previous answer, replacing it with this:
Get-ChildItem $directory -recurse:$recurse
foreach {
$file = ($directory + $_.FileName)
(Get-Content $file) | Foreach-object {
$_ -replace ([Regex]::Escape("[$pattern]")),"[$replace]")
} | Set-Content $file
}
Note:
The parentheses around Get-Content to ensure the file is slurped in one go (and therefore closed).
The piping to subsequent commands rather than inlining.
Some of your commands have been removed to ensure it's a simple test.
Just a suggestion but you might try looking at the documentation for the parameters code block. There is a more efficient way to ensure that a parameter is entered if you require it and to throw an error message if the user doesn't.
About_throw: http://technet.microsoft.com/en-us/library/dd819510.aspx
About_functions_advanced_parameters: http://technet.microsoft.com/en-us/library/dd347600.aspx
And then about using Write-Host all the time: http://powershell.com/cs/blogs/donjones/archive/2012/04/06/2012-scripting-games-commentary-stop-using-write-host.aspx
Alright, I finally sat down and just typed everything sequentially in PowerShell, then used that to make my script.
It was actually really simple;
$items = Get-ChildItem $directory -recurse:$recurse
$items |
foreach {
$file = $_.FullName
$content = get-content $file
$newContent = $content -replace $pattern, $replace
Set-Content $file $newcontent
}
Thanks for all your help guys.