ConvertTo-EnhancedHTML : Parameter set cannot be resolved using the specified named parameters - powershell

The code:
$Bios = Get-WmiObject -class Win32_BIOS | Select-Object -Property PSComputerName,Manufacturer,BIOSVersion | ConvertTo-EnhancedHTMLFragment -As List
$Bios2 = Get-WmiObject -class Win32_BIOS | Select-Object -Property PSComputerName,Manufacturer,BIOSVersion | ConvertTo-EnhancedHTMLFragment -As Table
ConvertTo-EnhancedHTML -HTMLFragments $Bios,$Bios2 | Out-File AsExample.html
The error:
ConvertTo-EnhancedHTML : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:1
+ ConvertTo-EnhancedHTML -HTMLFragments $Bios,$Bios2 | Out-File AsExamp ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [ConvertTo-EnhancedHTML], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,ConvertTo-EnhancedHTML
I don't understand why I am receiving the error. I've examined the help, and what I'm doing seems like it ought to be fair game:
-HTMLFragments <String[]>
One or more HTML fragments, as produced by ConvertTo-EnhancedHTMLFragment.
-HTMLFragments $part1,$part2,$part3
Required? true
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false

I have resolved the error by specifying an additional parameter:
ConvertTo-EnhancedHTML -HTMLFragments $Bios,$Bios2 -CSSStyleSheet syles.css | Out-File AsExample.html
While reviewing the 2 available parameter sets for ConvertTo-EnhancedHTML, I noticed that there was only 1 parameter required in each one -- -HTMLFragments. The error was telling me it didn't know which parameter set I wanted to use. So what I ended up having to do was specify the -CSSStylesheet parameter, as that parameter and its opposing -CSSUri parameter are the only parameters unique to their respective sets. In other words, the command wouldn't execute because I had to specify one of the CSS parameters so powershell knew which parameter set to use DESPITE THE FACT THAT neither of the CSS parameters are actually required parameters. Wow!

Related

How to use previous command output as a parameter in Powershell?

I need to write a Powershell one-liner to do a complex task. I need it to be strictly one line because I want to run it in a Go and Python script. The task requires taking the output of the first command and use it as a parameter in the second command.
I thought it was a simple task but I am struggling with it quite a bit. For example, the below command does not work:
$obj = Get-Item . | Get-Item $obj.Root | Format-List *
Get-Item : The input object cannot be bound to any parameters for the command either because the command does not take
pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
At line:1 char:21
+ $obj = Get-Item . | Get-Item $obj.Root | Format-List *
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (C:\Users\fhcat:PSObject) [Get-Item], ParameterBindingException
+ FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.GetItemCommand
What am I doing wrong?
Strictly one line sounds like a personal preference rather than a technical requirement.
However, you can end a statement with a semicolon or process the input via ForEach-Object
$obj = Get-Item .; Get-Item $obj.Root | Format-List *
or
Get-Item . | ForEach-Object {Get-Item $_.Root} | Format-List *

Checking to see if PowerShell Command worked, Crap Error

Needed to clean up a winmail.dat issue by adding a contact and setting a couple of parameters. All worked as shown, except the command to test that it worked.
Get-MailContact | Select randomemail#email.com | Select -UseMapiRichTextFormat
What is the reason for this failure?
Select-Object : A parameter cannot be found that matches parameter
name 'UseMapiRichTextFormat'. At line:1 char:62 + ... t 1 Select
support#solidpe.maxdesk.us 1 Select -UseMapiRichTextFormat +
---------------------- + CategoryInfo : InvalidArgument: (:) [Select-Object], ParameterBindingException + FullyQualifiedErrorId :
NamedParameterNotFound,Microsoft.PowerShell.Commands.SelectObjectCommand
I think what you actually need is this:
Get-MailContact |
Where-Object {$_.ExternalEmailAddress -eq 'randomemail#email.com'} |
Select-Object -Property UseMapiRichTextFormat
Where-Object limits the result set by comparing each contact's ExternalEmailAddress property and only including the objects that match in the output (which should only be one)
Select-Object limits the output object members to a subset of the original members

Invoke-Command on remote session returns local values

Question
Should the script block of Invoke-Command, when run with a PSSession, always run on the remote computer?
Context
I ran the below powershell against a list of servers:
Clear-Host
$cred = get-credential 'myDomain\myUsername'
$psSessions = New-PSSession -ComputerName #(1..10 | %{'myServer{0:00}' -f $_}) -Credential $cred
Invoke-Command -Session $psSessions -ScriptBlock {
Get-Item -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters'
} | Sort-Object PSComputerName
# $psSessions | Remove-PSSession
This returned:
Hive: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos
Name Property PSComputerName
---- -------- --------------
Parameters MaxPacketSize : 1 myServer01
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer02
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer03
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer04
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer05
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer06
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer07
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer08
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer09
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer10
MaxTokenSize : 65535
All looks good; onlyl I'd not expected to see these values / I was running this as a quick sense check before setting the values on these servers to ensure I didn't overwrite anything.
I had a quick look at one of the servers using regedit; and found that MaxTokenSize and MaxPacketSize did not exist.
I then amended the command to use Get-ItemProperty instead of Get-Item:
Invoke-Command -Session $psSessions -ScriptBlock {
Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters' -Name 'MaxTokenSize'
} | Sort-Object PSComputerName
This time I got 10 errors:
Property MaxTokenSize does not exist at path HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters.
+ CategoryInfo : InvalidArgument: (MaxTokenSize:String) [Get-ItemProperty], PSArgumentException
+ FullyQualifiedErrorId : System.Management.Automation.PSArgumentException,Microsoft.PowerShell.Commands.GetItemPropertyCommand
+ PSComputerName : myServer01
# ... (and the same for the other 9 servers, with only PSComputerName changing)
Regarding where the values that were returned came from... they're from my local machine. Amending my local registry entries and rerunning the original command showed all "servers" as having the new value.
I'm guessing this is a bug; but because I've not played with PSSessions much so far wanted to check here in case it's an issue with my understanding / usage of these commands, or if there are known gotchas to watch out for when using PSSessions.
tl;dr:
The root cause is a bug in the formatting instructions for registry keys (as of Windows PowerShell 5.1.18362.125 and PowerShell Core 7.0.0-preview.2) leading to the unexpected mix of remote and local information - see GitHub issue #10341.
As an aside: Similarly, PowerShell's ETS (extended type system) can introduce problems too, as shown in Mathias' answer here.
The best workaround is to simply use Get-ItemProperty (without a -Name argument) instead of Get-Item.
Mathias R. Jessen has provided the crucial pointer in a comment on the question, and js2010's answer provides a limited workaround and a pointer to the root cause, but it's worth providing more background information:
PowerShell comes with formatting instructions for type Microsoft.Win32.RegistryKey, as output by Get-Item with a registry path.
These formatting instructions define a calculated column named Property for the default (tabular) view, which helpfully shows a summary of the output registry key's values, which involves accessing the registry again, using Get-ItemProperty as shown in js2010's answer.
However, that behind-the-scenes Get-ItemProperty call always accesses the local registry - even when the keys were retrieved from a different machine, via PowerShell remoting, so you'll end up with a spurious mix of remote and local information.
Note that, technically, when Get-Item is run remotely, what you receive locally is an approximation of the original Microsoft.Win32.RegistryKey object, due to the serialization and deserialization involved in remoting. This approximation is a custom object with static copies of the original object's property values, and its (simulated) type name is Deserialized.Microsoft.Win32.RegistryKey - note the prefix.
PowerShell applies formatting instructions based on the full type name of output objects, but in the absence of a specific instructions or a given Deserialized.<originalTypeName> type, PowerShell applies the instructions for <originalTypeName>, which is what causes the problems here.
A - cumbersome, but edition-agnostic[1] - way to see the problematic formatting instruction is to run the following command:
(Get-FormatData Microsoft.Win32.RegistryKey -PowerShellVersion $PSVersionTable.PSVersion).FormatViewDefinition.Control | % {
$colNames = $_.Headers.Label
$colValues = $_.Rows.Columns.DisplayEntry.Value
foreach ($i in 0..($colNames.Count-1)) {
[pscustomobject] #{
ColumnName = $colNames[$i]
ColumnValue = $colValues[$i]
}
}
} | Format-Table -Wrap
This yields the column names and definitions for the table view:
ColumnName ColumnValue
---------- -----------
Name PSChildName
Property
$result = (Get-ItemProperty -LiteralPath $_.PSPath |
Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider |
Format-List | Out-String | Sort).Trim()
$result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
if($result.Length -eq 5000) { $result += "..." }
$result
The workaround suggested in js2010's answer - piping to Format-Table * or Format-List * is effective in the sense that it prevents the inapplicable local information from being displayed: by specifying properties explicitly (even by wildcard pattern *), only those properties are displayed on output - not also the flawed calculated column.
However, while the true Property property of the output objects provides access to the value names in the registry key at hand, it doesn't provide the actual data, the way that the calculated Property column does.
By contrast, using Get-ItemProperty without a -Name argument in lieu of Get-Item as a workaround returns both value names and data (correctly even when remoting) and even does so without restrictions (whereas Get-Item limits output to 5000 chars.)
The output format will be slightly different, but all the information is there.
[1] That is, the command works also in PowerShell Core, where the built-in formatting instructions are no longer maintained as external *.format.ps1xl files and are instead compiled into the executable.
Pipe it to fl * or ft * so it doesn't use the format file to display the registry keys. The format file runs get-itemproperty locally to try to display the properties.
From the bottom of $PSHOME\Registry.format.ps1xml for type Microsoft.Win32.RegistryKey:
<ScriptBlock>
$result = (Get-ItemProperty -LiteralPath $_.PSPath |
Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider |
Format-List | Out-String | Sort).Trim()
$result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
if($result.Length -eq 5000) { $result += "..." }
$result
</ScriptBlock>

calling a variable in select -expandproperty

I'm trying to make a report that lists a server name, vm name, and notes section from the vm but I cannot seem to get this code to run, it always gives me this error:
Select-Object : Cannot convert 'System.Object[]' to the type
'System.String' required by parameter 'ExpandProperty'. not supported.
At C:\Cooper\Jobs\Get-VmNotes - Copy.ps1:32 char:48 + get-vm -server
FA0150 | Select -expandproperty $server, Name, Notes +
~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument:
(:) [Select-Object], ParameterBindingException +
FullyQualifiedErrorId :
CannotConvertArgument,Microsoft.PowerShell.Commands.SelectObjectCommand
I can get the vmname and notes to output together but I just want to have a column that lists the vcenter server it is associated with.
Expand property is like the same as saying (Get-VM -Server FA0150).Name. It expands the property name you are selecting. You were trying to expand 3 properties (System.Object[]), but it is looking for just a string name of the property you want to expand. Use -Property instead.
get-vm -server FA0150 | Select-Object -Property Name,Notes
get-vm -server FA0150 | Select-Object -Property Name,Notes | Format-Table -Autosize
To also include the server name I made it into a script form since it can no longer be a one-liner:
[CmdletBinding()]
Param ( $Server )
$VMs = Get-VM -Server $Server
$VMs | Select-Object -Properties #(
#{ Label = 'Server';Expression = { $Server } }
'Name'
'Notes'
)
I found a solution to my problem using New-ViProperty. The only problem is now it creates four separate csv files and I want to combine them all into one based on the filename, keeping the same format, and delete the four others based on their filename. Is there an easy way to do this?

Why don't errors get returned when calling properties that don't exist?

Given the following snippet
$drives = Get-PSDrive
foreach($drive in $drives)
{
Write-Host $drive.Name "`t" $drive.Root
Write-Host " - " $drive.Free "`t" $drive.PropertyDoesntExist
}
The drive.PropertyDoesntExist property doesn't... erm... exist so I would expect an error to be thrown but instead it returns a null.
How can I get errors or exceptions?
EDIT - Me bad - I asked 2 questions in one so I moved one into a separate question.
The NextHop Blog provides a good solution to this problem. It doesn't give you an error, but instead a boolean. You can use Get-Member to get a collection of all of the real properties of the object's type and then match for your desired property.
Here's an example for strings:
PS C:\> $test = "I'm a string."
PS C:\> ($test | Get-Member | Select-Object -ExpandProperty Name) -contains "Trim"
True
PS C:\> ($test | Get-Member | Select-Object -ExpandProperty Name) -contains "Pigs"
False
If you explicitly want an error, you may want to look into Set-Strictmode as Set-StrictMode -version 2 to trap non-existent properties. You can easily turn it off when you're done with it, too:
PS C:\> Set-StrictMode -version 2
PS C:\> "test".Pigs
Property 'Pigs' cannot be found on this object. Make sure that it exists.
At line:1 char:8
+ "test". <<<< Pigs
+ CategoryInfo : InvalidOperation: (.:OperatorToken) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFoundStrict
PS C:\> Set-StrictMode -off