Piping parameters into cmdlet - powershell

This was done for testing and we have the solution but I would like to dig deeper.
I have a file list.txt that contains names of computers:
name
computer1
computer2
computer3
...
when I try
Import-Csv .\list.txt | Select-Object -property #{n="computername";e={$_.name}} | Get-Service
we get an error "Get-Service : Cannot find any service with service name '#{computername=computer1}'." Which I understand as Get-Service trying to map computername=computer1 to the parameter "name" (name="computername=computer1") even though the parameter "computername" is specified.
My solution was to add the "name" parameter and it works as expected
Import-Csv .\list.txt | Select-Object -property #{n="computername";e={$_.name}} | Get-Service -name *
My question is, why? Get-Service should accept pipeline input byPropertyName and it recognizes computername. Why doesn't it bind it unless I specify another parameter? Neither "name", nor "computername" is required. Also
Get-Service
and
Get-Service -computername "computer1"
both work without specifying "name".

Related

Using PowerShell Invoke-Command not providing same IIS info when commands ran locally trying to get SSLBinding

I can't for the life of me figure out how to get my code to work remotely to show the same information it's showing when ran locally.
For example, if I run the command locally on a web server:
Get-ChildItem IIS:SSLBindings
I get the following results:
But if I run the command remotely using the following code:
Invoke-command -computer $Computer { Import-Module WebAdministration; Get-Childitem -Path IIS:\SslBindings }
I get this result:
I don't understand why the Sites info is blank, or just showing '...'.
I've tried all sorts of different variations/scriptblocks, but the results are always the same.
Anyone have any idea as to what I'm doing wrong or how I can remotely pull this information correctly?
I feel like there may be a better way to do this because this feels a bit clunky, but regardless, it works...
Here's the command I am using to gather this info remotely:
$SSLCertInUseInfo = Invoke-command -computer $Computer {
Import-Module WebAdministration; Get-Childitem -Path IIS:\SslBindings | Select IPAddress, Host, Port, Store,
#{ Name = 'Site'; Expression = { $_ | select -property Sites -expandproperty Sites | Select-Object -ExpandProperty "Value" } }
} | Select -Property * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
The result is:
Why this particular property is an issue: The cause for this is how the value for Sites is generated. This particular property happens to be a "ScriptProperty," which means it's pulled by a script defined in the WebAdministration module. That script is executed behind the scenes transparently. Unfortunately, ScriptProperties often don't survive the deserialization process when accessed through PSRemoting.
So, how do you find out if the property is a ScriptProperty? Check the member definitions by piping your command to Get-Member.
When run locally, you can see that the Sites member type is a ScriptProperty and the definition shows the start of the script it runs to fetch the data.
PS C:\> Get-Childitem -Path IIS:\SslBindings | Get-Member Sites
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Sites ScriptProperty System.Object Sites {get=$ip = [string]::empty...
When run remotely, you can see the type changes to one prefixed with "Deserialized," the member type is now a NoteProperty, and the definition changes to a string with no value.
PS C:\> Invoke-Command -ComputerName $Computer { Import-Module WebAdministration;Get-Childitem -Path IIS:\SslBindings } | Get-Member Sites
TypeName: Deserialized.System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Sites NoteProperty System.String Sites=
How to solve the problem: The easiest way to get the desired value is to use calculated properties to convert the output to something that can be sent back. Similar to this answer, but a little more compact:
Invoke-Command -ComputerName $Computer {
Import-Module WebAdministration; Get-Childitem -Path IIS:\SslBindings |
Select-Object IPAddress, Port, Host, Store, #{Name="Sites"; Expression={($_).Sites.Value}} } |
Select-Object * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
Output:
IPAddress : ::
Port : 443
Host :
Store : MY
Sites :
IPAddress : 0.0.0.0
Port : 443
Host :
Store : My
Sites : Default Web Site

Getting list of specific attributes for all Hyper-V VMs on Windows Server

Let's start off on the right foot. I'm not looking for someone to write the script discussed below for us. What I'd like is a pointer or pointers that can quickly get me up to speed on just the PowerShell parts I need in order to accomplish this task without first becoming an expert in PowerShell and then in use of Get-VM. That said, please read on.
We are attempting to do a virtual machine inventory on several VM hosts in a network of computers. To that end, we would like to write a script that writes a .CSV file containing the host name, and specific attributes for each VM defined on that host. So, for example:
HostName, VMName, ComputerName, ID, Maximum memory, Minimum meomry, ...
VMH1, VM1, DATACENTER1, 54324ebe-f167-..., 1099511627776, 536870912, ...
and so on, for each VM under this host... and we'd run the command on each of our hosts to get the inventory into files we could merge for an enterprise view.
Where I need help, is with PowerShell scripting. I'm highly skilled with KornShell and many other scripting languages, but I've avoided PS all these years. Now the piper is at my door.
Trying to understand how to tie these two things together:
I can get a list of VMs with the
Get-VM | ft Name
Given a VM, I can get the details I want with
Get-VM <vmname> |
Select-Object Name, etc...
I believe I'd need to pipe the command into the command, but that's not working for me. And then once that's done, need to manipulate the output so that %COMPUTERNAME% is at the front of the output, so a simple Export-Csv won't work for us either.
Again, I don't want this to be a "here's my homework - do it for me" entry; just looking for pointers.
THanks for your time.
All of what you are after are in the examples in the PowerShell Help Files for that cmdlet.
# Get specifics for a module, cmdlet, or function
(Get-Command -Name Get-VM).Parameters
(Get-Command -Name Get-VM).Parameters.Keys
Get-help -Name Get-VM -Examples
# Results
<#
Get-VM -ComputerName Server1 |
Where-Object {$_.State -eq 'Running'}
Get-ClusterGroup |
Where {$PSItem.GroupType -eq 'VirtualMachine' } |
Get-VM
#>
Get-help -Name Get-VM -Full
Get-help -Name Get-VM -Online
As for your code in the comment section, this ...
Get-VM |
ForEach-Object Get-VM |
Select-Object *
and/or this ...
Get-VM |
ft Name|
ForEach-Object Get-VM |
Select-Object *
... both are syntactically wrong. If you use the PowerShell_ISE or VSCode editor they would immediately flag these as errors. If you are prone to staying in the console host, then use the Invoke-ScriptAnalyzer cmdlet to check your code and Trace-Command to see all it is doing. See the help file for details.
(Get-Command -Name Invoke-ScriptAnalyzer).Parameters
(Get-Command -Name Invoke-ScriptAnalyzer).Parameters.Keys
Get-help -Name Invoke-ScriptAnalyzer -Examples
Get-help -Name Invoke-ScriptAnalyzer -Full
Get-help -Name Invoke-ScriptAnalyzer -Online
(Get-Command -Name Trace-Command).Parameters
(Get-Command -Name Trace-Command).Parameters.Keys
Get-help -Name Trace-Command -Examples
Get-help -Name Trace-Command -Full
Get-help -Name Trace-Command -Online
Thus should be this
Get-VM |
ForEach-Object {
$PSItem |
Select-Object -Property '*'
}
Never do '*' for all properties, unless that is what you really want. Always select only what you need.
To see all the properties of on the first VM, just to this
(Get-VM)[0] |
Select-Object -Property '*'
To see all the properties of all the VM's, just remove the [0].
You easily leverage the Out-GtidView cmdlet for selecting VM and viewing details.
(Get-VM)[0] |
Select-Object -Property '*' |
Out-GridView -Title 'Showing Virtual Machines'
see:
https://mcpmag.com/articles/2016/02/17/creating-a-gui-using-out-gridview.aspx
https://spiderzebra.com/2020/05/26/utilizing-powershell-out-gridview-as-a-gui-alternative
https://mikefrobbins.com/2014/09/11/creating-a-simplistic-gui-interface-with-out-gridview
Lastly, you are also saying, you want a list of VMHosts, that you are then trying to get the VM Guest off those hosts. That is not what Get-VM is for.
If your VMHosts are Windows Servers, joined to your domain, you'd use Get-ADComputer to get a list of VMHost names (however you have them named), then pass that to Get-VM to get the guests.
Example:
Get-ADComputer -Identity 'VMHost*' |
ForEach-Object {Get-VM -ComputerName $PSitem}
If they are not windows machines, use your chosen method to get that hostname, then pass that to Get-VM.
Doing this on my local Hyper-V machine
$env:COMPUTERNAME, 'Localhost' |
ForEach {
Get-VM -ComputerName $PSItem |
Select-Object -Property Name, State, Status, Version
}
# Results
<#
Name State Status Version
---- ----- ------ -------
WS2K16 Off Operating normally 9.0
...
#>
$env:COMPUTERNAME, 'Localhost' |
ForEach-Object {
$VMHostName = $PSItem
Get-VM -ComputerName $VMHostName |
Select-Object -Property #{
Name = 'VMHost'
Expression = {$VMHostName }
}, Name, State, Status, Version |
Export-Csv -Path 'D:\Temp\VMGuestReport.Csv' -Append
}
Import-Csv -Path 'D:\Temp\VMGuestReport.Csv' |
Format-Table -AutoSize
# Results
<#
VMHost Name State Status Version
------ ---- ----- ------ -------
SRV1 WS2K16 Off Operating normally 9.0
...
Localhost WS2K16 Off Operating normally 9.0
...
#>

strange behavior with wcf and powershell

Working with Powershell and web service. With powershell I am running and updating a cirensen form . When the powershell compiles, I am calling back the service request Id and updating one property of the form.
Working fine with powershell.
With web servcie I am getting error:
"A parameter cannot be found that matches parameter name \'eq\'."
Get-SCSMObject -Class $sRClass -filter “ID -eq $serviceRequestId” | Set-SCSMObject -Property Source -Value “Other”
I don't think the -Filter parameter works like that!
Sounds like you probably want to use the Where-Object CmdLet:
Get-SCSMObject -Class $sRClass |
Where-Object {$_.ID -eq $serviceRequestId} |
Set-SCSMObject -Property Source -Value “Other”

PowerShell: Service enumeration based on part of service name from multiple machines

I know that I can use PowerShell to check service status on multiple services. For example with something like this:
Get-Service -ComputerName server-a, server-b, server-c -Name MyService |
Select Name, MachineName, Status
Can somebody advice how I can modify this so that:
- Enumerate large number of servers like an array or somehow else so that make it more readable than if I put large number of servers in one line.
- Use a wildcard in service name parameter, e.g. "MYSERVICE*"
You can put your servers in a text file such as SERVERS.TXT :
Server-a
Server-b
Server-c
...
And use :
$servers = get-content SERVERS.TXT
Get-Service -ComputerName $servers -Name MyService | Select Name, MachineName, Status
You can do the same for services.
To answer your second question first, the -Name parameter of the Get-Service cmdlet supports wildcards, so you can simply do this to check several services with similar names:
Get-Service -Computer 'server-a', 'server-b', 'server-c' -Name MyService* |
select Name, MachineName, Status
The -Computer parameter accepts an array of strings, so you can read the server list from a file (containing one hostname per line), as JPBlanc suggested:
$computers = Get-Content 'C:\path\to\serverlist.txt'
Get-Service -Computer $computers -Name MyService* | ...
This is probably the best choice, as it separates data from code.
However, there are situations where it's more practical to keep data and code in the same file (e.g. when you move the script around a lot). In a situation like that you can define an array spanning multiple lines like this:
$computers = 'server-a',
'server-b',
'server-c',
...
Get-Service -Computer $computers -Name MyService* | ...

Get startup type of Windows service using PowerShell

How can I get the Windows service startup type using PowerShell and not using WMI?
I looked inside the Get-Service command, and it does not provide something to display the "startup type".
With PowerShell version 4:
You can run a command as given below:
Get-Service | select -property name,starttype
WMI is the way to do this.
Get-WmiObject -Query "Select StartMode From Win32_Service Where Name='winmgmt'"
Or
Get-WmiObject -Class Win32_Service -Property StartMode -Filter "Name='Winmgmt'"
In PowerShell you can use the command Set-Service:
Set-Service -Name Winmgmt -StartupType Manual
I haven't found a PowerShell command to view the startup type though. One would assume that the command Get-Service would provide that, but it doesn't seem to.
You can use also:
(Get-Service 'winmgmt').StartType
It returns just the startup type, for example, disabled.
As far as I know there is no “native” PowerShell way of getting this information. And perhaps it is rather the .NET limitation than PowerShell.
Here is the suggestion to add this functionality to the version next:
https://connect.microsoft.com/PowerShell/feedback/details/424948/i-would-like-to-see-the-property-starttype-added-to-get-services
The WMI workaround is also there, just in case. I use this WMI solution for my tasks and it works.
Once you've upgraded to PowerShell version 5 you can get the startup type.
To check the version of PowerShell you're running, use $PSVersionTable.
The examples below are for the Windows Firewall Service:
For the local system
Get-Service | Select-Object -Property Name,Status,StartType | where-object {$_.Name -eq "MpsSvc"} | Format-Table -auto
For one remote system
Get-Service -ComputerName HOSTNAME_OF_SYSTEM | Select-Object -Property MachineName,Name,Status,StartType | where-object {$_.Name -eq "MpsSvc"} | Format-Table -auto
For multiple systems (must create the systems.txt)
Get-Service -ComputerName (Get-content c:\systems.txt) | Select-Object -Property MachineName,Name,Status,StartType | where-object {$_.Name -eq "MpsSvc"} | Format-Table -auto
Use:
Get-Service BITS | Select StartType
Or use:
(Get-Service -Name BITS).StartType
Then
Set-Service BITS -StartupType xxx
[PowerShell 5.1]
If you update to PowerShell 5 you can query all of the services on the machine and display Name and StartType and sort it by StartType for easy viewing:
Get-Service |Select-Object -Property Name,StartType |Sort-Object -Property StartType
You can also use the sc tool to set it.
You can also call it from PowerShell and add additional checks if needed.
The advantage of this tool vs. PowerShell is that the sc tool can also set the start type to auto delayed.
# Get Service status
$Service = "Wecsvc"
sc.exe qc $Service
# Set Service status
$Service = "Wecsvc"
sc.exe config $Service start= delayed-auto
It is possible with PowerShell 4.
Get-Service *spool* | select name,starttype | ft -AutoSize
screenshot
By default StartType is not shown by Get-Service, but you can always explicitly ask for it:
Get-Service | select StartType,DisplayName | sort StartType,DisplayName
Use Get-Service | Get-Member to see all available fields.