Get-Service results to array - powershell

I'm sure this should be straight forward but I've been stuck on it for a while now...
I am trying to get the service names (for sql server) into an array but can't figure out how to do it. I basically want the array contents to look something like the output of this:
Get-Service -computername $server_name -name sql* | format-table -property name
I have tried things like this but the contents of $service_name are very odd:
$service_name = (Get-Service -computername $server_name -name sql* -exclude *sqlwriter | format-table -property name)
Whatever I try either errors or gives some strange message in my array. Should this be easy/possible? I'm guessing I could dump the results in a text file then use the contents of that but it's a bit messy and more overhead than seems necessary.

Not sure, but where you looking for this (with whatever variation on the pattern for -Name and/or -Exclude)?
PS> $names = (Get-Service -Name Sql* | select name)
PS> $names.GetType().IsArray
True
PS> $names.Length
3
PS> $names
Name
----
SQLBrowser
SQLSERVERAGENT
SQLWriter
Or even along the lines of the following if you really want the "Name" as a System.String array.
PS> $names = (Get-Service -Name Sql* | foreach { $_.Name -as [string]})
PS> $names[0].GetType().FullName
System.String
PS> $names
SQLBrowser
SQLSERVERAGENT
SQLWriter
But also keep the good advice in #alroc's answer in mind - maybe you want to keep the actual type of Get-Service's result (System.ServiceProcess.ServiceController) as long as possible and access/use the Name property of it as late as possible. YMMV.

You're using format-table in the pipeline. Anytime you use a format-* cmdlet, that's the end of the line for your data - it's now just a formatted bunch of text, it's no longer data that you can actually use.
Try this to get the names formatted as a table for viewing:
$service_name = (Get-Service -computername $server_name -name sql* -exclude *sqlwriter);
$service_name | format-table -property name;
That said, I ran both your version and mine above and got the same visual output - the critical difference is that mine stores data in $service_name, not just a bunch of text (as yours does).
edit:
In response to:
All I really needed was the service names in an array to then use elsewhere
$service_name = (Get-Service -computername $server_name -name sql* -exclude *sqlwriter) | select-object -expandproperty name;
or:
$service_name = (Get-Service -computername $server_name -name sql* -exclude *sqlwriter).name;
No need to invoke WMI directly.

Related

Get multiple fields back?

Is it possible to get multiple properties back for a command within parentheses? Consider the following code.
$service = (get-service -name 'wuauserv')
$service.Name # I get the name property back
But what If I wanted to get more than one property. For example below:
$service.Name,Status
Why doesn't this work? Is there a way to do it?
What you are dealing with is the concept of how PowerShell handles outputting information and the pipeline. When you collect Get-Service into the variable $service, you are storing an object that has multiple properties.
To work with the properties of a given object (one or many) you utilize Select-Object (docs). Whether you are dealing with that variable contents or directly with the output from Get-Service you have to pipe the output to Select-Object to retrieve one to many properties. You can do this multiple ways. PowerShell works on positions when it comes to parameters, so the position 0 parameter for Select-Object is -Property. All of the following are equivalent, and just various ways to get the same result:
$proc = Get-Service -Name mssql*
$proc | Select-Object Name, DisplayName
$proc = Get-Service -Name mssql*
Select-Object -Property Name, DisplayName -InputObject $proc
Get-Service -Name mssql* | Select-Object Name, DisplayName
If you want your variable to only contain a given set of properties then you would also utilize Select-Object:
$proc = Get-Service -Name mssql* | Select-Object Name, DisplayName
$proc
Output Example:
When you do $Service.Name it returns a the expanded property, how would you return the expanded property for multiple properties?
PS H:\> $Service = (Get-Service -name 'wuauserv')
PS H:\> $Service | Select-Object Name, Status
Name Status
---- ------
wuauserv Stopped
Notice how they are not an expanded property.
PS H:\> $service.Name
wuauserv
PS H:\> $service | Select-Object -ExpandProperty Name
wuauserv
Trying to expand multiple properties will end up with an error as the method cannot accept multiple arguments (Name, Status).

-PipelineVariable isnt working as intended

I have this script that changes services per a csv file input
Import-CSV .\SSAS_services.csv |
ForEach-Object{
Get-Service $_.Service -ComputerName $_.Server -PipelineVariable svc|
Set-Service -Status $_.Task -StartupType $_.'Startup Type' -PassThru
} |
Select-Object MachineName, Name, Status, StartType, #{n='OldStatus';e={$svc.Status}}, #{n='OldStartType';e={$svc.StartType}} |
tee-object -FilePath '.\ChangeServices_LOG.txt' #-Append
Server,Service,Startup Type,Task
DCVPIM108,SQL Server Analysis Services (MSSQLSERVER),automatic,start
server2,"SQL Server Analysis Services (MSSQLSERVER), SQL Server Analysis Services (MSSQLSERVER) CEIP",Manual,stop
it works great, except for my -PipelineVariable svcis not working as intended. if a service was "stopped" and "Manual" before being changed to "running" and "automatic", it doesnt get the old values "stopped" and "Manual" for OldStatus and OldStartType
MachineName : DCVPIM108
Name : MSSQLServerOLAPService
Status : Running
StartType : Automatic
OldStatus : Running
OldStartType : Automatic
why is that?
The -PipelineVariable / -pv common parameter only works:
within a single pipeline.
in script blocks in later segments of the same pipeline.
Since you're using it in a pipeline that is nested inside the ForEach-Object script block, the commands in the outer pipeline cannot use it.
However, I suggest restructuring your command so that you don't need a pipeline variable for Get-Service anymore.
Instead,
-PipelineVariable $csvRow is used with Import-Csv, so that you can more easily refer to it even in nested pipelines (the alternative would be to define the variable explicitly at the start of the ForEach-Object script block as $csvRow = $_).
$svc is then declared as an -OutVariable, so that the original service state is captured before Set-Service is called to change it.
Getting a service, setting its startup type, and enriching the CSV-row object with additional information now all happen inside the ForEach-Object script block.
Import-CSV .\SSAS_services.csv -PipelineVariable csvRow | ForEach-Object {
Get-Service -Name $csvRow.Service -ComputerName $csvRow.Server -OutVariable svc |
Set-Service -Status $csvRow.Task -StartupType $csvRow.'Startup Type'
$csvRow | Select-Object MachineName, Name, Status, StartType,
#{n='OldStatus';e={$svc.Status}},
#{n='OldStartType';e={$svc.StartType}}
} | Tee-object -FilePath '.\ChangeServices_LOG.txt'
I guess what you want is to pass same object down the multiple pipes. I haven't use -PipeLineVariable much, but looks like it just creating a nicer alias for $_ . If you need to push something specific down the pipeline I guess you need to use write-ouput with custom object or hashtable. Below is a dummy sample, pushing down and modifying a hastable:
$services = "xagt" , "xbgm" , "XblGameSave"
$list = new-object System.Collections.ArrayList
$serv | foreach {
$svc = Get-Service $_ ; Write-Output #{Name = $svc.Name; Stat=$svc.Status}
} | foreach {$_.SomeNewItem = "new stuff"; $list.Add($_)}
But in your case one pipeline might be sufficient. Try something like that:
Import-CSV .\SSAS_services.csv | foreach {
$old = Get-Service $_.Service;
Set-Service -Name $_.Service -Status Running
$new = Get-Service $_.Service;
$data = $_.MachineName, $_.Service, $old.Status, $new.Status -join ","
Write-Host $data
$data >> Log.txt
}

Azure PowerShell cmdlet into Strings

Just a quick question. I am running lines of code like,
$publicIP = Get-Content (Get-AzureVM -ServiceName $servicename -Name $vmsname | Get-AzureEndpoint | Select { $._vip })
$OSDisk = Get-Content (Get-AzureVM -ServiceName $servicename -Name $vmsname | Get-AzureOSDisk)
And it does get me the IPAddress or the variables, but when trying to put the value into a csv cell, it fails. I think it is because it tries to add the extra labels, but I do not know how to get just the string and set the variable as just that. Does anyone know how to remedy this?
edit:
If the output with:
Get-AzureVM -servicename "vm1" -name "vm1" | Select DNSName | Out-String
is like this:
DNSName
-------
http://example.cloudapp.net/
How do I just put in "http://example.cloudapp.net/" as a CSV-entry? Right now it is trying to put all of the code block into the CSV which is giving me awful formatting erros.
Select is an alias for Select-Object and allows you to take one or more properties from an object and creates a new object with them.
If you want to expand a property into just the value, use the -ExpandProperty parameter:
Get-AzureVM -servicename "vm1" -name "vm1" | Select -ExpandProperty DNSName
Should give you just http://example.cloudapp.net/
Also, when you want to send data to a CSV, you can pipe objects to the cmdlet Export-CSV which will use the property names as headers and the objects in the array as rows and the properties as columns.
Example:
Get-ChildItem -File | Select Name, Size | Export-Csv c:\temp\filelist.csv

Formatting the results of a powershell script

How can i add the server name at the left of each line result on this script?. Thank you!
$servers = Get-Content -path .\Machines.txt
[pscustomobject]$result = #()
$subresult =
ForEach ($server in $servers)
{
Set-Service -computername $servers -Name sacsvr -StartupType Disabled -PassThru
}
$result = $subresult
$result | Out-File local_group_members.csv
This is an example result:
Status Name DisplayName
------ ---- -----------
Stopped sacsvr Special Administration Console Helper
Stopped sacsvr Special Administration Console Helper
Stopped sacsvr Special Administration Console Helper
Alternatively you can just add a property to the objects you're outputting right now. Pipe your Set-Service to Add-Member like this:
Set-Service -computername $servers -Name sacsvr -StartupType Disabled -PassThru | Add-Member -MemberType NoteProperty -Name 'Server' -Value $Server -PassThru
Now each object that you pass to $subresult has a new property Server that is the name of the server it was run on. You'll probably want to pipe through Select when outputting to have the order you want.
$SubResult | Select Server, Status, Name, DisplayName | Export-CSV 'local_group_members.csv' -NoType
You can arbitrarily re-order or add to your output with Select-Object. You can use hash tables to include calculated properties such as your desired ServerName.
So for each server, you can set the services and tag the output with that server name:
ForEach ($server in $servers)
{
Set-Service -computername $server -Name sacsvr -StartupType Disabled -PassThru |
Select #{Name = 'ServerName'; Expression = {$server}}, Name, DisplayName, Status
}
The above is shorthand for:
Select-Object -Property (properties)
The -Property parameter allows you to select any arbitrary grouping of properties on the type of object being piped in. Another parameter, -InputObject allows us to pipe in objects by value.

Exporting results of foreach loop to CSV

The script reads in a .TXT file of computer names. It then checks to see if Windows Update is set to Auto or Manual; the computers name and update type are then exported to a CSV. At first, it was only exporting the final computer and it's type. I did some research and saw that creating an object to hold the name and type and then adding that object to an array would work. However, exporting the array to a CSV results in interesting data which is related to the array instead of the computer name and type. On top of that, I have the array printed out in the console pre-export and it looks to be the last computer and its type listed 9 times, the amount of computers on the one .TXT file I'm reading in. I know export-CSV now has an append parameter that could take care of this but we have much more PS 1.0 and 2.0 in the office and need it to work on those version.
clear-host
$item = new-object PSObject
$list = #()
$c=Get-Credential
$a = gc "c:\Scripts\pcList.txt"
ForEach ($b in $a)
{
$pcName = Get-WmiObject win32_service -filter 'name="wuauserv"' -computer $b -credential $c -ErrorAction SilentlyContinue |
select-object name,
#{N="System Name";E={$_.Systemname}}
$name = $pcName.'System Name'
$item | Add-Member NoteProperty Name $name -Force
$pcType = Get-WmiObject win32_service -filter 'name="wuauserv"' -computer $b - credential $c -ErrorAction SilentlyContinue |
select-object name, #{N="Startup Type";E={$_.StartMode}}
$type = $pcType.'Startup Type'
$item | Add-Member NoteProperty StartupType $type -Force
$list += $item
}
Write-Host $list
export-csv -Path "c:\Scripts\pcListCSV" -inputobject $list -notype
Ok, a couple things. The script looks fairly good really, all things considered, and a couple little tweaks and it should work fine. Let's approach this a tad different and just construct the object all at once instead of making it then adding noteproperties.
Also, we'll just make 1 call to GWMI (short for Get-WMIObject). I think this will work better for you. First off, making the GWMI call once means we have to select more than one thing when we make it. Since you are really making the exact same call twice, and selecting two different things from it each time, this just kind of makes sense. We're going to skip defining things in a select, and just let $pcName store all the data from the GWMI call. Though, first we're going to make sure that there's no old data in $pcName just to make sure we don't get duplicates.
clear-host
$item = new-object PSObject
$list = #()
$c=Get-Credential
$a = gc "c:\Scripts\pcList.txt"
ForEach ($b in $a)
{
if(Get-Variable pcName -ea SilentlyContinue){Remove-Variable pcName}
$pcName = Get-WmiObject win32_service -filter 'name="wuauserv"' -computer $b -credential $c -ErrorAction SilentlyContinue
Ok, now we create the object, and we'll just add it directly to the array. Here's where we define things like Name and StartupType as object properties, so when you display the array later it shows those two properties for each item. After that we'll close the ForEach loop and both display the list to the screen and pipe it to Export-CSV to save the results.
$list += New-Object PSObject -Property #{
Name = $pcName.SystemName
StartupType = $pcName.StartMode
}
}
$list
$list | Export-Csv "C:\Scripts\pcList.CSV" -notype
So, this doesn't check to make sure that the systems are online, or do any error checking like that, but if that's OK with you then I think this should do exactly what you need.