I have two functions one creates a custom object which once done is piped to the next function. The problem is that the second function is not receiving my object correctly. Instead of using the pipeline I have tried setting a variable and then piping that variable to the function. Below are the two functions with the output of get member on the returned object. All the string param are being processed correctly. But the objects simply wont work. In the begin block of the New-BaseGuest I am not able to assign the results to the variables.
Basically I want to end up with:
Get-ServerFromXML -XMLFile File -GuestName Name | New-BaseGuest
The New-BaseGuest function is incomplete as I cannot get the parameters working.
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
CDROM_Connected NoteProperty object CDROM_Connected=null
CDROM_ISO NoteProperty object CDROM_ISO=null
Floppy_Connected NoteProperty object Floppy_Connected=null
Floppy_ISO NoteProperty object Floppy_ISO=null
GuestType NoteProperty string GuestType=vmkernel6Guest
IPAddress NoteProperty string IPAddress=192.168.199.40
ServerName NoteProperty string ServerName=ESX-VSAN3
vCPU NoteProperty string vCPU=2
vHDD NoteProperty Object[] vHDD=System.Object[]
vNIC NoteProperty Object[] vNIC=System.Object[]
vRAM NoteProperty string vRAM=5120
function Get-ServerFromXML
{
<##>
[CmdletBinding()]
param
(
[String]
$XMLFile,
[String]
$GuestName
)
$xml = New-Object -TypeName XML
$xml.Load($XMLFile)
$Server = Select-Xml -Xml $xml -XPath ('/Servers/Server[#Name = "' + $GuestName + '" ]')
$vNicObjs = #()
$vHddobjs = #()
If ($Server.Node.Hardware.vnic.Count -ne $null)
{
$Server.Node.Hardware.vnic | %{
$NicProps = #{
NIC_ID = $_.ID;
NIC_Type = $_.Type;
NIC_StartConnected = $_.StartConnected;
NIC_MACAddress = $_.MACAddress;
NIC_WakeOnLan = $_.WakeOnLan;
NIC_PortGroup = $_.PortGroup
}
$vNicObj = New-Object -TypeName System.Management.Automation.PSObject -Property $NicProps
$vNicObjs += $vNicObj
}
}
Else
{
$NicProps = #{
NIC_ID = $Server.Node.Hardware.vnic.ID;
NIC_Type = $Server.Node.Hardware.vnic.Type;
NIC_StartConnected = $Server.Node.Hardware.vnic.StartConnected;
NIC_MACAddress = $Server.Node.Hardware.vnic.MACAddress;
NIC_WakeOnLan = $Server.Node.Hardware.vnic.WakeOnLan;
NIC_PortGroup = $Server.Node.Hardware.vnic.PortGroup
}
$vNivObj = New-Object -TypeName System.Management.Automation.PSObject -Property $NicProps
$vNicObjs += $vNivObj
}
If ($Server.Node.Hardware.vHDD.Count -ne $null)
{
$Server.Node.Hardware.vHDD | %{
$HDDProps = #{
HDD_ID = $_.ID;
HDD_Type = $_.Type;
HDD_Size = $Server.Node.Hardware.vHDD.Size;
HDD_Datastore = $_.Datastore;
HDD_StorageFormate = $_.StorageFormat
}
$vHDDObj = New-Object -TypeName System.Management.Automation.PSObject -Property $HDDProps
$vHDDObjs += $vHDDObj
}
}
Else
{
$HDDProps = #{
HDD_ID = $Server.Node.Hardware.vHDD.ID;
HDD_Type = $Server.Node.Hardware.vHDD.Type;
HDD_Size = $Server.Node.Hardware.vHDD.Size;
HDD_DataStore = $Server.Node.Hardware.vHDD.Datastore;
HDD_StorageFormat = $Server.Node.Hardware.vHDD.StorageFormat
}
$vHDDObj = New-Object -TypeName System.Management.Automation.PSObject -Property $HDDProps
$vHDDObjs += $vHDDObj
}
$ServerProps =[Ordered]#{
ServerName = $Server.Node.Description.Name;
GuestType = $Server.Node.Description.GuestType;
IPAddress = $Server.Node.Description.PrimaryIP;
vCPU = $Server.Node.Hardware.vCPU;
vRAM = $Server.Node.Hardware.vRAM;
vHDD = $vHddobjs;
vNIC = $vNicObjs;
CDROM_ISO = $Server.Node.Hardware.CDROM.ISOPath
CDROM_Connected = $Server.Node.Hardware.CDROM.StartConnected
Floppy_ISO = $Server.Node.Hardware.Floppy.ISOPath
Floppy_Connected = $Server.Node.Hardware.Floppy.StartConnected
}
$ServerObj = New-Object -TypeName System.Management.Automation.PSObject -Property $ServerProps
$ServerObj
}
function New-BaseGuest
{
<##>
[CmdletBinding(PositionalBinding = $true,
SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory = $true,
ValueFromPipeline = $false,
ValueFromPipelineByPropertyName = $true,
Position = 1)]
[string]
$ServerName,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 2)]
[string]
$GuestType,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 3)]
[string]
$vCPU,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 4)]
[string]
$vRAM,
[Parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true,
Position = 5)]
[system.object]
$vHDD,
[Parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true,
Position = 6)]
[system.object]
$vNIC,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 7)]
[string]
$CDROM_ISO = $Null,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 8)]
[string]
$FLOPPY_ISO = $Null,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 9)]
[string]
$CDROM_Connected = $Null,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 10)]
[string]
$Floppy_Connected = $Null
)
BEGIN
{
$Datastore = ($vHDD | Where-Object { $_.HDD_ID -eq 1 } | Select-Object -Property HDD_DataStore).HDD_DataStore
$PortGroup = ($vNIC | Where-Object { $_.NIC_ID -eq 1 } | Select-Object -Property NIC_PortGroup).NIC_PortGroup
$DiskSize = ($vHDD | Where-Object { $_.HDD_ID -eq 1 } | Select-Object -Property HDD_Size).HDD_Size
$DiskFormat = ($vHDD | Where-Object { $_.HDD_ID -eq 1 } | Select-Object -Property HDD_StorageFormat).HDD_StorageFormat
}
PROCESS
{
New-VM -Name $ServerName `
-Datastore $Datastore `
-NumCPU $vCPU `
-MemoryGB $vRAM`
-DiskGB $DiskSize `
-NetworkName $PortGroup `
-DiskStorageFormat $CDROM_ISO `
-GuestID $GuestType | Out-Null
}
END
{
}
}
This is expected behavior. The begin {} block runs before any pipeline objects are encountered, so you have no access to any parameters that come in through the pipeline in your begin block.
The process {} block is run once for each item, so the code you have in the begin block really needs to be put there (because it's specific to a single VM).
Related
I am using a custom function that imports data using an API. The function requires several different parameters, which I am currently defining with one $Params hashtable. From there I am adding other parameters that may or may not be added based on certain conditions. I have noticed that storing all parameters in the hashtable is not working properly, but will only work if particularly specified in the function call.
A basic version of the code that does not currently work is:
$ExtraParam = 'ccc'
$Params = #{
Par1 = 'aaa'
Par2 = 'bbb'
}
$Params.Par3 = $ExtraParam
Import-APIData #Params
The API is returning the following error message:
{"errorId":"error.request.invalidRepresentation.malformed","errorCode":0,"message":"Invalid parameter value was specified."}
However, if I run the following code it works perfectly fine:
$ExtraParam = 'abc'
$Params = #{
Par1 = '123'
Par2 = '456'
}
Import-APIData #Params -Par3 $ExtraParam
I have verified that the parameters coming into the function are all of the correct type. The custom function I am calling is proprietary so I cannot share the entire function, but I was just wondering if there was anything that would reformat the parameters in the hashtable as opposed to explicitly calling it in the function call. Given that the two chunks of code are essentially the same I am hoping someone can point out some fundamental difference that may cause the top portion to not work.
Per a request of a commenter, here is the actual function. The _CallAPI function at the bottom is what actually calls the API. I will not be able to share that, but using the $Params output right above it (commented out in the code) may hopefully be enough to troubleshoot.
function Update-ICUser {
<#
.SYNOPSIS
Updates user on a Gensys ICWS server.
.PARAMETER ICSession
string ICSession. ICSession to update a user.
.PARAMETER Id
string Id. Id of the user to update.
.PARAMETER InputObject
object InputObject. Full IC User is needed.
.PARAMETER Argument
string Argument. Any query string parameters associated with the Api call.
.Example
$ICSession = New-ICSession -ComputerName server1 -Credential $Credential
Get-ICUser -Id user1 -Full |Update-ICUser -ICSession $ICsession -Extension 1234
Example 1: Update a user's extension.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory,
Position = 0)]
[ICSession]$ICSession,
[Parameter(Mandatory,
ValueFromPipeline,
ParameterSetName = 'InputObject',
Position = 1)]
[object]$InputObject,
[Parameter(Mandatory,
ParameterSetName = 'Manual',
Position = 2)]
[string]$Id,
[Parameter(ParameterSetName = 'Manual',
Position = 3)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$DisplayName,
[Parameter(ParameterSetName = 'Manual',
Position = 4)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$OutboundAni,
[Parameter(ParameterSetName = 'Manual',
Position = 5)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$Title,
[Parameter(ParameterSetName = 'Manual',
Position = 6)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$OfficeLocation,
[Parameter(ParameterSetName = 'Manual',
Position = 7)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$Notes,
[Parameter(ParameterSetName = 'Manual',
Position = 8)]
[Parameter(ParameterSetName = 'InputObject')]
[int]$Cost = 0,
[Parameter(ParameterSetName = 'Manual',
Position = 9)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$AutoAnswerAcdInteractions = $false,
[Parameter(ParameterSetName = 'Manual',
Position = 10)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$AutoAnswerNonAcdInteractions = $true,
[Parameter(ParameterSetName = 'Manual',
Position = 11)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$ApplicationId = 'InteractionDesktop',
[Parameter(ParameterSetName = 'Manual',
Position = 12)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$ExcludeFromDirectory = $false,
[Parameter(ParameterSetName = 'Manual',
Position = 13)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$Extension,
[Parameter(ParameterSetName = 'Manual',
Position = 14)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$FaxCapability = $false,
[Parameter(ParameterSetName = 'Manual',
Position = 15)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$OutlookIntegrationEnabled = $false,
[Parameter(ParameterSetName = 'Manual',
Position = 16)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$FirstName,
[Parameter(ParameterSetName = 'Manual',
Position = 17)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$LastName,
[Parameter(ParameterSetName = 'Manual',
Position = 18)]
[Parameter(ParameterSetName = 'InputObject')]
[string[]]$WorkgroupList,
[Parameter(ParameterSetName = 'Manual',
Position = 19)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$DepartmentName,
[Parameter(ParameterSetName = 'Manual',
Position = 20)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$City,
[Parameter(ParameterSetName = 'Manual',
Position = 21)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$PostalCode,
[Parameter(ParameterSetName = 'Manual',
Position = 22)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$State,
[Parameter(ParameterSetName = 'Manual',
Position = 23)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$StreetAddress,
[Parameter(ParameterSetName = 'Manual',
Position = 24)]
[Parameter(ParameterSetName = 'InputObject')]
[int]$HomeSite,
[Parameter(ParameterSetName = 'Manual',
Position = 25)]
[Parameter(ParameterSetName = 'InputObject')]
[string[]]$LicenseList,
[Parameter(ParameterSetName = 'Manual',
Position = 26)]
[Parameter(ParameterSetName = 'InputObject')]
[string[]]$RoleList
)
Try {
if ($InputObject) {
$User = $InputObject
$Id = $InputObject.configurationId.id
}
else {
$User = Get-ICUser -ICSession $ICSession -Id $Id -Full
}
'Id','InputObject','ICSession' |Foreach-Object {
$PSBoundParameters.Remove($PSItem) |Out-Null
}
$PSBoundParameters.GetEnumerator() |Foreach-Object {
if ($PSItem.Key -in ('Title','DepartmentName','City','State','StreetAddress','FirstName','LastName','Notes')) {
if ($PSItem.Key -in ('State','FirstName','LastName')) {
$Key = switch ($PSItem.Key) {
'State' {'stateOrProvince'}
'FirstName' {'givenName'}
'LastName' {'surName'}
}
}
else {
$Key = $PSItem.Key
}
$User.personalInformationProperties.$Key = $PSItem.Value
}
elseif ($PSItem.Key -in ('WorkgroupList','LicenseList','RoleList')) {
$Key = $PSItem.Key
if ($Key = 'WorkgroupList' -and $WorkgroupList) {
[array]$Value = Get-ICWorkgroup -ICSession $SessionKey -Id $WorkgroupList
$User.Workgroups = $Value
}
if ($Key = 'LicenseList' -and $LicenseList) {
$Value = #((Get-ICLicenseAllocation -ICSession $SessionKey -Id $LicenseList).configurationId)
if ($User.licenseProperties.PSobject.Properties.name -match 'additionalLicenses') {
$User.licenseProperties.additionalLicenses = $Value
}
else {
$User.licenseProperties | Add-Member -MemberType NoteProperty -Name 'additionalLicenses' -Value $Value -Force
}
}
if ($Key = 'RoleList' -and $RoleList) {
[array]$Value = Get-ICRole -ICSession $SessionKey -Id $RoleList
if ($User.roles.PSobject.Properties.name -match 'actualValue') {
$User.Roles.actualValue = $Value
}
else {
$User.roles | Add-Member -MemberType NoteProperty -Name 'actualValue' -Value $Value -Force
}
}
}
else {
$Key = $PSItem.Key
$Value = $PSItem.Value
$User |Add-Member -MemberType NoteProperty -Name $Key -Value $Value -Force
}
}
$Body = $User |ConvertTo-Json -Depth 5
$Params = #{
Area = $Configuration.Area.Configuration
Resource = $Configuration.Resource.Users
ICSession = $ICSession
Id = $Id
Method = 'Put'
ContentType = 'application/json'
Body = $Body
}
# $Params
_CallApi #Params
}
Catch {
_ExceptionError
}
}
I will also provide everything on the console. In this example the $AutoAnswerAcd parameter is the one that is failing, but I have gotten it to fail and work by adding/removing other parameters. I have not detected a pattern that makes it fail yet, other than that it always works outside of the splat. You can see this pattern below.
PS C:\> $SessionKey = New-ICSession -ComputerName $server -Credential $Credential
PS C:\> $Params = #{
>> ICSession = $SessionKey
>> Id = 'tautomation'
>> WorkgroupList = "MultiSite-UserSpan"
>> RoleList = "Business User"
>> LicenseList = "I3_ACCESS_RECORDER"
>> DepartmentName = "Infr & Ops"
>> Title = "Process Automation Engineer"
>> OfficeLocation = "Remote"
>> AutoAnswerAcdInteractions = $true
>> }
PS C:\> Update-ICUser #Params
Exception: C:\Repos\ICTools\Code\Private\_ExceptionError.ps1:10
Line |
10 | throw "$($Exception)`n$($Exception.Exception.Message)"
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| {"errorId":"error.request.invalidRepresentation.malformed","errorCode":0,"message":"Invalid parameter value was specified."} Response status code does not indicate
| success: 400 (Bad Request). {"errorId":"error.request.invalidRepresentation.malformed","errorCode":0,"message":"Invalid parameter value was specified."} Response status
| code does not indicate success: 400 (Bad Request).
PS C:\> $Params = #{
>> ICSession = $SessionKey
>> Id = 'tautomation'
>> WorkgroupList = "MultiSite-UserSpan"
>> RoleList = "Business User"
>> LicenseList = "I3_ACCESS_RECORDER"
>> DepartmentName = "Infr & Ops"
>> Title = "Process Automation Engineer"
>> OfficeLocation = "Remote"
>> }
PS C:\> Update-ICUser #Params
id uri
-- ---
tautomation /configuration/users/tautomation
PS C:\> Update-ICUser #Params -AutoAnswerAcdInteractions $true
id uri
-- ---
tautomation /configuration/users/tautomation
PS C:\>
Thanks!
Ok, so think I've found what's happening.
Firstly, here's an instrumented version of your code that gives some log output as it runs - that'll help see where it's going wrong...
function Update-ICUser {
[CmdletBinding()]
param (
[Parameter(Mandatory,
Position = 0)]
[string]$ICSession,
[Parameter(Mandatory,
ValueFromPipeline,
ParameterSetName = 'InputObject',
Position = 1)]
[object]$InputObject,
[Parameter(Mandatory,
ParameterSetName = 'Manual',
Position = 2)]
[string]$Id,
[Parameter(ParameterSetName = 'Manual',
Position = 3)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$DisplayName,
[Parameter(ParameterSetName = 'Manual',
Position = 4)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$OutboundAni,
[Parameter(ParameterSetName = 'Manual',
Position = 5)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$Title,
[Parameter(ParameterSetName = 'Manual',
Position = 6)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$OfficeLocation,
[Parameter(ParameterSetName = 'Manual',
Position = 7)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$Notes,
[Parameter(ParameterSetName = 'Manual',
Position = 8)]
[Parameter(ParameterSetName = 'InputObject')]
[int]$Cost = 0,
[Parameter(ParameterSetName = 'Manual',
Position = 9)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$AutoAnswerAcdInteractions = $false,
[Parameter(ParameterSetName = 'Manual',
Position = 10)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$AutoAnswerNonAcdInteractions = $true,
[Parameter(ParameterSetName = 'Manual',
Position = 11)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$ApplicationId = 'InteractionDesktop',
[Parameter(ParameterSetName = 'Manual',
Position = 12)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$ExcludeFromDirectory = $false,
[Parameter(ParameterSetName = 'Manual',
Position = 13)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$Extension,
[Parameter(ParameterSetName = 'Manual',
Position = 14)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$FaxCapability = $false,
[Parameter(ParameterSetName = 'Manual',
Position = 15)]
[Parameter(ParameterSetName = 'InputObject')]
[bool]$OutlookIntegrationEnabled = $false,
[Parameter(ParameterSetName = 'Manual',
Position = 16)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$FirstName,
[Parameter(ParameterSetName = 'Manual',
Position = 17)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$LastName,
[Parameter(ParameterSetName = 'Manual',
Position = 18)]
[Parameter(ParameterSetName = 'InputObject')]
[string[]]$WorkgroupList,
[Parameter(ParameterSetName = 'Manual',
Position = 19)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$DepartmentName,
[Parameter(ParameterSetName = 'Manual',
Position = 20)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$City,
[Parameter(ParameterSetName = 'Manual',
Position = 21)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$PostalCode,
[Parameter(ParameterSetName = 'Manual',
Position = 22)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$State,
[Parameter(ParameterSetName = 'Manual',
Position = 23)]
[Parameter(ParameterSetName = 'InputObject')]
[string]$StreetAddress,
[Parameter(ParameterSetName = 'Manual',
Position = 24)]
[Parameter(ParameterSetName = 'InputObject')]
[int]$HomeSite,
[Parameter(ParameterSetName = 'Manual',
Position = 25)]
[Parameter(ParameterSetName = 'InputObject')]
[string[]]$LicenseList,
[Parameter(ParameterSetName = 'Manual',
Position = 26)]
[Parameter(ParameterSetName = 'InputObject')]
[string[]]$RoleList
)
if ($InputObject) {
$User = $InputObject
$Id = $InputObject.configurationId.id
}
else {
$User = [pscustomobject] #{
personalInformationProperties = [pscustomobject] #{
DepartmentName = $null
Title = $null
}
licenseProperties = [pscustomobject] #{}
roles = [pscustomobject] #{}
Workgroups = $null
}
#Get-ICUser -ICSession $ICSession -Id $Id -Full
}
#write-host "user = '$($User |ConvertTo-Json -Depth 5)'"
'Id','InputObject','ICSession' |Foreach-Object {
$PSBoundParameters.Remove($PSItem) |Out-Null
}
$PSBoundParameters.GetEnumerator() |Foreach-Object {
write-host "processing key '$($PSItem.Key)'"
if ($PSItem.Key -in ('Title','DepartmentName','City','State','StreetAddress','FirstName','LastName','Notes')) {
if ($PSItem.Key -in ('State','FirstName','LastName')) {
$Key = switch ($PSItem.Key) {
'State' {'stateOrProvince'}
'FirstName' {'givenName'}
'LastName' {'surName'}
}
}
else {
$Key = $PSItem.Key
}
$User.personalInformationProperties.$Key = $PSItem.Value
}
elseif ($PSItem.Key -in ('WorkgroupList','LicenseList','RoleList')) {
$Key = $PSItem.Key
if ($Key = 'WorkgroupList' -and $WorkgroupList) {
write-host " WorkgroupList - casting to array"
#$Value = #( Get-ICWorkgroup -ICSession $SessionKey -Id $WorkgroupList )
[array] $Value = #()
#$User.Workgroups = $Value
}
if ($Key = 'LicenseList' -and $LicenseList) {
write-host " LicenseList - casting to array"
#$Value = #((Get-ICLicenseAllocation -ICSession $SessionKey -Id $LicenseList).configurationId)
$Value = #()
if ($User.licenseProperties.PSobject.Properties.name -match 'additionalLicenses') {
$User.licenseProperties.additionalLicenses = $Value
}
else {
$User.licenseProperties | Add-Member -MemberType NoteProperty -Name 'additionalLicenses' -Value $Value -Force
}
}
if ($Key = 'RoleList' -and $RoleList) {
write-host " RoleList - casting to array"
#[array]$Value = Get-ICRole -ICSession $SessionKey -Id $RoleList
[array]$Value = #()
if ($User.roles.PSobject.Properties.name -match 'actualValue') {
$User.Roles.actualValue = $Value
}
else {
$User.roles | Add-Member -MemberType NoteProperty -Name 'actualValue' -Value $Value -Force
}
}
}
else {
write-host "adding key '$($PSItem.Key)' with value '$($PSItem.Value)'"
write-host " value before is '$($Value.GetType().FullName)'"
$Key = $PSItem.Key
write-host " value after is '$($Value.GetType().FullName)'"
$Value = $PSItem.Value
#write-host ($PSItem.Key | convertto-json)
#write-host ($PSItem.Value | convertto-json)
#write-host $PSItem.Key.GetType().FullNane
#write-host $PSItem.Value.GetType().FullNane
#write-host "user before = '$($User |ConvertTo-Json -Depth 5)'"
$User |Add-Member -MemberType NoteProperty -Name $Key -Value $Value -Force
#write-host "user after = '$($User |ConvertTo-Json -Depth 5)'"
}
}
$Body = $User |ConvertTo-Json -Depth 5
write-host $Body
}
and when you call it with, e.g. this:
$Params = #{
ICSession = "aaa"
Id = 'tautomation'
WorkgroupList = "MultiSite-UserSpan"
RoleList = "Business User"
LicenseList = "I3_ACCESS_RECORDER"
DepartmentName = "Infr & Ops"
Title = "Process Automation Engineer"
OfficeLocation = "Remote"
AutoAnswerAcdInteractions = $true
}
Update-ICUser #Params
you get this output:
processing key 'DepartmentName'
processing key 'OfficeLocation'
adding key 'OfficeLocation' with value 'Remote'
processing key 'Title'
processing key 'WorkgroupList'
WorkgroupList - casting to array
LicenseList - casting to array
RoleList - casting to array
processing key 'AutoAnswerAcdInteractions'
adding key 'AutoAnswerAcdInteractions' with value 'True'
value before is 'System.Object[]'
value after is 'System.Object[]'
processing key 'RoleList'
WorkgroupList - casting to array
LicenseList - casting to array
RoleList - casting to array
processing key 'LicenseList'
WorkgroupList - casting to array
LicenseList - casting to array
RoleList - casting to array
{
"personalInformationProperties": {
"DepartmentName": "Infr & Ops",
"Title": "Process Automation Engineer"
},
"licenseProperties": {
"additionalLicenses": []
},
"roles": {
"actualValue": []
},
"Workgroups": null,
"OfficeLocation": "Remote",
"AutoAnswerAcdInteractions": [
true
]
}
So there's a few things to note:
casting to array
casting to array seems to be getting logged for three parameters (WorkgroupList, RoleList and LicenseList) when processing each of them.
If you look, your code is doing this: if ($Key = 'WorkgroupList' -and $WorkgroupList) but = is an assignment operator, not a comparison operator, so what you really want is if ($Key -eq 'WorkgroupList' -and $WorkgroupList)
It's the same for if ($Key = 'LicenseList' -and $LicenseList) and if ($Key = 'RoleList' -and $RoleList) - change those to -eq as well and that's one bug stomped...
adding key 'AutoAnswerAcdInteractions' with value 'True'
The $Value variable is of type System.Object[] before and after assigning the parameter value. If you look at the previous parameter in the logs you'll see WorkgroupList - casting to array - this is changing the type of $Value to be an array. Any future attempts to assign values will automatically convert the value to an array, so $true becomes [ $true ], which is where your problem is coming from.
Note the same cast is happening in RoleList, but not LicenseList.
Since the order of keys is not guaranteed in a hashtable or $PSBoundParameters, your issue only happens if:
$WorkgroupList or $RoleList are specified in the function call
They happen to be enumerated before $AutoAnswerAcdInteractions
To fix this, refactor WorkgroupList and RoleList to be the same as LicenseList - i.e. instead of:
[array]$Value = <expression>
use the Array subexpression operator:
$Value = #( <expression> )
This will ensure the value stored in the variable is an array without changing the type of the variable for future parameters.
Note that the reason the extra parameter appears to work is it changes the order of the items in $PSBoundParameters. For example:
$Params = #{
ICSession = "aaa"
Id = 'tautomation'
WorkgroupList = "MultiSite-UserSpan"
RoleList = "Business User"
LicenseList = "I3_ACCESS_RECORDER"
DepartmentName = "Infr & Ops"
Title = "Process Automation Engineer"
OfficeLocation = "Remote"
}
Update-ICUser #Params -AutoAnswerAcdInteractions $true
gives this output
processing key 'AutoAnswerAcdInteractions'
adding key 'AutoAnswerAcdInteractions' with value 'True'
processing key 'DepartmentName'
processing key 'Title'
processing key 'WorkgroupList'
WorkgroupList - casting to array
processing key 'RoleList'
RoleList - casting to array
processing key 'LicenseList'
LicenseList - casting to array
processing key 'OfficeLocation'
adding key 'OfficeLocation' with value 'Remote'
value before is 'System.Object[]'
value after is 'System.Object[]'
{
"personalInformationProperties": {
"DepartmentName": "Infr & Ops",
"Title": "Process Automation Engineer"
},
"licenseProperties": {
"additionalLicenses": []
},
"roles": {
"actualValue": []
},
"Workgroups": null,
"AutoAnswerAcdInteractions": true,
"OfficeLocation": [
"Remote"
]
}
Note that AutoAnswerAcdInteractions is processed before any of the paths that change the type of $Value to an array.
Incidentally, the code also works if you omit WorkgroupList and RoleList because $Value never gets changed to an array:
$Params = #{
ICSession = "aaa"
Id = 'tautomation'
#WorkgroupList = "MultiSite-UserSpan"
#RoleList = "Business User"
LicenseList = "I3_ACCESS_RECORDER"
DepartmentName = "Infr & Ops"
Title = "Process Automation Engineer"
OfficeLocation = "Remote"
AutoAnswerAcdInteractions = $true
}
Update-ICUser #Params
with the output:
processing key 'OfficeLocation'
adding key 'OfficeLocation' with value 'Remote'
processing key 'Title'
processing key 'AutoAnswerAcdInteractions'
adding key 'AutoAnswerAcdInteractions' with value 'True'
value before is 'System.String'
value after is 'System.String'
processing key 'DepartmentName'
processing key 'LicenseList'
LicenseList - casting to array
{
"personalInformationProperties": {
"DepartmentName": "Infr & Ops",
"Title": "Process Automation Engineer"
},
"licenseProperties": {
"additionalLicenses": []
},
"roles": {},
"Workgroups": null,
"OfficeLocation": "Remote",
"AutoAnswerAcdInteractions": true
}
This a function for changing local user password on a remote machine. I'd like to make it work with a value from pipeline. This works:
$x= #()
$x += Set-UserPassword -ComputerName smz0017d -User localadmin -NewPassword "1"
$x += Set-UserPassword -ComputerName smz0027d -User localadmin -NewPassword "2"
$x | Out-GridView
But with value from pipeline it doesn't. Any tips?
$x = #()
$x += [pscustomobject]#{
ComputerName = 'smzmi0027d'
User = 'localadmin'
NewPassword = 'djkufdjkuf1234'
}
$x += [pscustomobject]#{
ComputerName = 'smzmi0027d'
User = 'localadmin'
NewPassword = '1'
}
foreach ($y in $x)
{
$y | Set-UserPassword
}
The function to accept value from a pipeline. It invokes command on a remote machine and builds custom object with a result:
function Set-UserPasswordLocaly
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true,
Position = 0)]
[string]$User,
[Parameter(Mandatory = $true,
Position = 1)]
[string]$NewPassword
)
#TODO: Place script here
try
{
Set-LocalUser -Name $User -Password (ConvertTo-SecureString $NewPassword -AsPlainText -Force) -ErrorAction Stop
$pwdSetResult = "$user password has been changed"
$isSuccess = $true
}
catch
{
$pwdSetResult = ($_.Exception).Message
$isSuccess = $false
}
return [PSCustomObject]#{
'ComputerName' = $env:COMPUTERNAME
'isSuccess' = $isSuccess
'Message' = $pwdSetResult
}
}
function Set-UserPasswordRemotely
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true,
Position = 0)]
[string]$ComputerName,
[Parameter(Mandatory = $true,
Position = 1)]
[string]$User,
[Parameter(Mandatory = $true,
Position = 2)]
[string]$NewPassword
)
#TODO: Place script here
$param = #{
ComputerName = $ComputerName
ScriptBlock = ${function:Set-UserPasswordLocaly}
ArgumentList = $User, $NewPassword
ErrorAction = 'stop'
}
try
{
$invoke = Invoke-Command #param
$invokeResult = $invoke.Message
$isSuccess = $invoke.isSuccess
}
catch
{
$invokeResult = ($_.Exception).Message
$isSuccess = $false
}
return [PSCustomObject]#{
'ComputerName' = $ComputerName
'isSuccess' = $isSuccess
'Message' = $invokeResult
}
}
function Set-UserPassword
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true,
Position = 0)]
[string]$ComputerName,
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 1)]
[string]$User,
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 2)]
[string]$NewPassword,
[Parameter(Position = 3,ValueFromPipelineByPropertyName = $true)]
[string]$PasswordVersion
)
#TODO: Place script here
PROCESS
{
if ($env:COMPUTERNAME -eq $ComputerName)
{
Set-UserPasswordLocaly $User $NewPassword
}
else
{
Set-UserPasswordRemotely $ComputerName $User $NewPassword
}
}
}
The ComputerName parameter in Set-UserPassword is not configured to accept pipeline input. Change that and it'll work:
function Set-UserPassword
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 0)]
[string]$ComputerName,
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 1)]
[string]$User,
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 2)]
[string]$NewPassword,
[Parameter(Position = 3,ValueFromPipelineByPropertyName = $true)]
[string]$PasswordVersion
)
# I'm using Write-Host to demonstrate binding behavior, replace with your actual code
process { Write-Host $PSBoundParameters }
}
PS ~> [pscustomobject]#{
>> ComputerName = 'smzmi0027d'
>> User = 'localadmin'
>> NewPassword = '1'
>> } |Set-UserPassword
>>
[NewPassword, 1] [User, localadmin] [ComputerName, smzmi0027d]
I'm unable to display the calculated property column called Logon Type which is translated from a hash table.
The script below is working fine, but I just need to translate the raw value number into a more meaningful description.
function Get-LogonEvents {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('ServerName', 'Server', 'Name')]
[string[]]$ComputerName,
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
[PSCredential]$Credential,
[Parameter()]
[ValidateSet("Service", "Interactive", "RemoteInteractive", "NetworkCleartext", "CachedInteractive", "Unlock", "NewCredentials", "Network", "*")]
[string[]]$LogonType = #("Interactive", "RemoteInteractive", "CachedInteractive"),
[string]$UserName,
[Parameter()]
[switch]$Oldest,
[Parameter()]
[int64]$MaxEvents,
[Parameter()]
[datetime]$StartTime = (Get-Date 1/1/1900),
[Parameter()]
[datetime]$StopTime = (Get-Date 1/1/2100)
)
Begin {
Function ParseEventMessage {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
$obj
)
Begin {
$defaultDisplaySet = 'TimeCreated', 'MachineName', 'TargetDomainName', 'TargetUserName'
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’, [string[]]$defaultDisplaySet)
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]#($defaultDisplayPropertySet)
$myHash = #{ }
}
Process {
([xml]($obj.ToXml())).event.eventdata.data | ForEach-Object { $myHash[$PSItem.name] = $PSItem.'#text' }
New-Object -TypeName PSObject -Property $myHash | ForEach-Object {
$PSItem.PSObject.TypeNames.Insert(0, "EventLogRecord.XMLParse")
$PSItem | Add-Member MemberSet PSStandardMembers $PSStandardMembers -PassThru |
Add-Member -MemberType NoteProperty -Name TimeCreated -Value $obj.timecreated -PassThru |
Add-Member -MemberType NoteProperty -Name MachineName -Value $obj.MachineName -PassThru
}
}
}
$hashLogonType = #{
"Interactive" = "2"
"Network" = "3"
"Service" = "5"
"Unlock" = "7"
"NetworkCleartext" = "8"
"NewCredentials" = "9"
"RemoteInteractive" = "10"
"CachedInteractive" = "11"
}
$filter = #"
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[
(EventID='4624')
and TimeCreated[#SystemTime>='{0}' and #SystemTime<='{1}']
]
and EventData[
Data[#Name='LogonType'] and ({2})
{3}
]
]
</Select>
</Query>
</QueryList>
"#
}
Process {
foreach ($obj in $ComputerName) {
if ($UserName) {
$joinUserName = "and Data[#Name='TargetuserName'] and (Data='{0}')" -f $UserName
}
$joinLogonType = ($LogonType | ForEach-Object { $hashLogonType[$PSItem] }) -replace '^', "Data='" -replace '$', "'" -join " or "
$objFilter = $filter -f (Get-Date $StartTime -Format s), (Get-Date $StopTime -Format s), $joinLogonType, $joinUserName
$hashEventParm = #{
ComputerName = $obj
FilterXml = $objFilter
}
if ($Credential) { $hashEventParm['Credential'] = $Credential }
if ($MaxEvents) { $hashEventParm['MaxEvents'] = $MaxEvents }
$objFilter | Write-Verbose
Get-WinEvent #hashEventParm | ParseEventMessage
}
}
End { }
}
$TargetDomainNameException = #('Window Manager','Font Driver Host')
$exceptionRegex = $TargetDomainNameException -join "|"
Get-LogonEvents -ComputerName 'Localhost' -MaxEvents 10 |
Where-Object { ($_.TargetDomainName -notmatch $exceptionRegex) } |
Select-Object WorkstationName,
TargetUserName,
TargetDomainName,
Type,
LogonType,
#{n ='LogonType'; e={$hashLogonType[[string]$_.LogonType]}},
#{n = 'Logon Type'; e = {$hashLogonType["$($_.LogonType)"]}},
ProcessName,
IPAddress,
#{n="Host Name"; e={([System.Net.Dns]::GetHostByAddress($_.IPaddress).Hostname)}},
TimeCreated |
Out-GridView
Error:
I have modifiedthe Calculated property like:
#{n = 'Logon Type'; e = {$hashLogonType["$($_.LogonType)"]}},
Somehow it is still not displaying the column "Logon Type", however, the raw value on LogonType column still showing as 10, 3 ,etc...?
I see two problems.
$hashLogonType is defined inside the function and won't be available in the global scope.
The keys for $hashLogonType are by [string] not by [int].
If you're able to modify the original function, you might consider adding a property where the string value of LogonType is saved.
Otherwise, keep a copy of $hashLogonType in your variable scope with integers as keys, and base your calculated property on that.
The easiest way to get what you want is to create your own hash table and use it in your pipeline.
# Create a hash table for your own use in your variable scope.
$myHashTable = #{
2 = "Interactive"
3 = "Network"
5 = "Service"
7 = "Unlock"
8 = "NetworkCleartext"
9 = "NewCredentials"
10 = "RemoteInteractive"
11 = "CachedInteractive"
}
# Shim object.
$exampleObject = [PSCustomObject]#{
LogonType = 2
WorkstationName = "myHost.example.com"
}
# Modify your pipeline to use your hash table.
$exampleObject |
Select-Object -Property WorkstationName, LogonType, #{label="Logon Title";expression={$myHashTable[$_.LogonType]}}
PS> ./Answer 02.ps1
WorkstationName LogonType Logon Title
--------------- --------- -----------
myHost.example.com 2 Interactive
In principle, it is possible to modify the original function. But, I don't have any data to test with. Maybe Doug can help. He seems to have access to an event log.
You would have to do two things.
Add a hash table with integer keys in scope for ParseEventMessage(). For example, add the hash table to ParseEventMessage()'s Begin block.
Where it says
Add-Member -MemberType NoteProperty -Name MachineName -Value $obj.MachineName -PassThru
Add another property by extending that pipeline:
Add-Member -MemberType NoteProperty -Name LogonTitle -Value {$myHashTable[$_.LogonType]} -PassThru
Edit: Yes Mike is absolutely correct, the hashtable was defined inside the get-logonevents function and not used. I've moved it out and now it should work.
I think you should reverse the assignment of the hashtable. Either as an int or a string should work then. I did it like this and it worked fine.
function Get-LogonEvents {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('ServerName', 'Server', 'Name')]
[string[]]$ComputerName,
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
[PSCredential]$Credential,
[Parameter()]
[ValidateSet("Service", "Interactive", "RemoteInteractive", "NetworkCleartext", "CachedInteractive", "Unlock", "NewCredentials", "Network", "*")]
[string[]]$LogonType = #("Interactive", "RemoteInteractive", "CachedInteractive"),
[string]$UserName,
[Parameter()]
[switch]$Oldest,
[Parameter()]
[int64]$MaxEvents,
[Parameter()]
[datetime]$StartTime = (Get-Date 1/1/1900),
[Parameter()]
[datetime]$StopTime = (Get-Date 1/1/2100)
)
Begin {
Function ParseEventMessage {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
$obj
)
Begin {
$defaultDisplaySet = 'TimeCreated', 'MachineName', 'TargetDomainName', 'TargetUserName'
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’, [string[]]$defaultDisplaySet)
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]#($defaultDisplayPropertySet)
$myHash = #{ }
}
Process {
([xml]($obj.ToXml())).event.eventdata.data | ForEach-Object { $myHash[$PSItem.name] = $PSItem.'#text' }
New-Object -TypeName PSObject -Property $myHash | ForEach-Object {
$PSItem.PSObject.TypeNames.Insert(0, "EventLogRecord.XMLParse")
$PSItem | Add-Member MemberSet PSStandardMembers $PSStandardMembers -PassThru |
Add-Member -MemberType NoteProperty -Name TimeCreated -Value $obj.timecreated -PassThru |
Add-Member -MemberType NoteProperty -Name MachineName -Value $obj.MachineName -PassThru
}
}
}
$filter = #"
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[
(EventID='4624')
and TimeCreated[#SystemTime>='{0}' and #SystemTime<='{1}']
]
and EventData[
Data[#Name='LogonType'] and ({2})
{3}
]
]
</Select>
</Query>
</QueryList>
"#
}
Process {
foreach ($obj in $ComputerName) {
if ($UserName) {
$joinUserName = "and Data[#Name='TargetuserName'] and (Data='{0}')" -f $UserName
}
$joinLogonType = ($LogonType | ForEach-Object { $hashLogonType[$PSItem] }) -replace '^', "Data='" -replace '$', "'" -join " or "
$objFilter = $filter -f (Get-Date $StartTime -Format s), (Get-Date $StopTime -Format s), $joinLogonType, $joinUserName
$hashEventParm = #{
ComputerName = $obj
FilterXml = $objFilter
}
if ($Credential) { $hashEventParm['Credential'] = $Credential }
if ($MaxEvents) { $hashEventParm['MaxEvents'] = $MaxEvents }
$objFilter | Write-Verbose
Get-WinEvent #hashEventParm | ParseEventMessage
}
}
End { }
}
$hashLogonType = #{
2 = "Interactive"
3 = "Network"
5 = "Service"
7 = "Unlock"
8 = "NetworkCleartext"
9 = "NewCredentials"
10 = "RemoteInteractive"
11 = "CachedInteractive"
}
$TargetDomainNameException = #('Window Manager','Font Driver Host')
$exceptionRegex = $TargetDomainNameException -join "|"
Get-LogonEvents -ComputerName 'Localhost' -MaxEvents 10 -OutVariable LogonEvents |
Where-Object { ($_.TargetDomainName -notmatch $exceptionRegex) } |
Select-Object WorkstationName,
TargetUserName,
TargetDomainName,
Type,
#{n="LogonType";e={$hashLogonType.[int]$_.logontype}},
ProcessName,
IPAddress,
#{n="Host Name"; e={([System.Net.Dns]::GetHostByAddress($_.IPaddress).Hostname)}},
TimeCreated |
Out-GridView
I have two functions one creates a custom object which once done is piped to the next function. The problem is that the second function is not receiving my object correctly. Instead of using the pipeline I have tried setting a variable and then piping that variable to the function. Below are the two functions with the output of get member on the returned object. All the string param are being processed correctly. But the objects simply wont work. In the begin block of the New-BaseGuest I am not able to assign the results to the variables.
Basically I want to end up with:
Get-ServerFromXML -XMLFile File -GuestName Name | New-BaseGuest
The New-BaseGuest function is incomplete as I cannot get the parameters working.
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
CDROM_Connected NoteProperty object CDROM_Connected=null
CDROM_ISO NoteProperty object CDROM_ISO=null
Floppy_Connected NoteProperty object Floppy_Connected=null
Floppy_ISO NoteProperty object Floppy_ISO=null
GuestType NoteProperty string GuestType=vmkernel6Guest
IPAddress NoteProperty string IPAddress=192.168.199.40
ServerName NoteProperty string ServerName=ESX-VSAN3
vCPU NoteProperty string vCPU=2
vHDD NoteProperty Object[] vHDD=System.Object[]
vNIC NoteProperty Object[] vNIC=System.Object[]
vRAM NoteProperty string vRAM=5120
function Get-ServerFromXML
{
<##>
[CmdletBinding()]
param
(
[String]
$XMLFile,
[String]
$GuestName
)
$xml = New-Object -TypeName XML
$xml.Load($XMLFile)
$Server = Select-Xml -Xml $xml -XPath ('/Servers/Server[#Name = "' + $GuestName + '" ]')
$vNicObjs = #()
$vHddobjs = #()
If ($Server.Node.Hardware.vnic.Count -ne $null)
{
$Server.Node.Hardware.vnic | %{
$NicProps = #{
NIC_ID = $_.ID;
NIC_Type = $_.Type;
NIC_StartConnected = $_.StartConnected;
NIC_MACAddress = $_.MACAddress;
NIC_WakeOnLan = $_.WakeOnLan;
NIC_PortGroup = $_.PortGroup
}
$vNicObj = New-Object -TypeName System.Management.Automation.PSObject -Property $NicProps
$vNicObjs += $vNicObj
}
}
Else
{
$NicProps = #{
NIC_ID = $Server.Node.Hardware.vnic.ID;
NIC_Type = $Server.Node.Hardware.vnic.Type;
NIC_StartConnected = $Server.Node.Hardware.vnic.StartConnected;
NIC_MACAddress = $Server.Node.Hardware.vnic.MACAddress;
NIC_WakeOnLan = $Server.Node.Hardware.vnic.WakeOnLan;
NIC_PortGroup = $Server.Node.Hardware.vnic.PortGroup
}
$vNivObj = New-Object -TypeName System.Management.Automation.PSObject -Property $NicProps
$vNicObjs += $vNivObj
}
If ($Server.Node.Hardware.vHDD.Count -ne $null)
{
$Server.Node.Hardware.vHDD | %{
$HDDProps = #{
HDD_ID = $_.ID;
HDD_Type = $_.Type;
HDD_Size = $Server.Node.Hardware.vHDD.Size;
HDD_Datastore = $_.Datastore;
HDD_StorageFormate = $_.StorageFormat
}
$vHDDObj = New-Object -TypeName System.Management.Automation.PSObject -Property $HDDProps
$vHDDObjs += $vHDDObj
}
}
Else
{
$HDDProps = #{
HDD_ID = $Server.Node.Hardware.vHDD.ID;
HDD_Type = $Server.Node.Hardware.vHDD.Type;
HDD_Size = $Server.Node.Hardware.vHDD.Size;
HDD_DataStore = $Server.Node.Hardware.vHDD.Datastore;
HDD_StorageFormat = $Server.Node.Hardware.vHDD.StorageFormat
}
$vHDDObj = New-Object -TypeName System.Management.Automation.PSObject -Property $HDDProps
$vHDDObjs += $vHDDObj
}
$ServerProps =[Ordered]#{
ServerName = $Server.Node.Description.Name;
GuestType = $Server.Node.Description.GuestType;
IPAddress = $Server.Node.Description.PrimaryIP;
vCPU = $Server.Node.Hardware.vCPU;
vRAM = $Server.Node.Hardware.vRAM;
vHDD = $vHddobjs;
vNIC = $vNicObjs;
CDROM_ISO = $Server.Node.Hardware.CDROM.ISOPath
CDROM_Connected = $Server.Node.Hardware.CDROM.StartConnected
Floppy_ISO = $Server.Node.Hardware.Floppy.ISOPath
Floppy_Connected = $Server.Node.Hardware.Floppy.StartConnected
}
$ServerObj = New-Object -TypeName System.Management.Automation.PSObject -Property $ServerProps
$ServerObj
}
function New-BaseGuest
{
<##>
[CmdletBinding(PositionalBinding = $true,
SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory = $true,
ValueFromPipeline = $false,
ValueFromPipelineByPropertyName = $true,
Position = 1)]
[string]
$ServerName,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 2)]
[string]
$GuestType,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 3)]
[string]
$vCPU,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 4)]
[string]
$vRAM,
[Parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true,
Position = 5)]
[system.object]
$vHDD,
[Parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true,
Position = 6)]
[system.object]
$vNIC,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 7)]
[string]
$CDROM_ISO = $Null,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 8)]
[string]
$FLOPPY_ISO = $Null,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 9)]
[string]
$CDROM_Connected = $Null,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 10)]
[string]
$Floppy_Connected = $Null
)
BEGIN
{
$Datastore = ($vHDD | Where-Object { $_.HDD_ID -eq 1 } | Select-Object -Property HDD_DataStore).HDD_DataStore
$PortGroup = ($vNIC | Where-Object { $_.NIC_ID -eq 1 } | Select-Object -Property NIC_PortGroup).NIC_PortGroup
$DiskSize = ($vHDD | Where-Object { $_.HDD_ID -eq 1 } | Select-Object -Property HDD_Size).HDD_Size
$DiskFormat = ($vHDD | Where-Object { $_.HDD_ID -eq 1 } | Select-Object -Property HDD_StorageFormat).HDD_StorageFormat
}
PROCESS
{
New-VM -Name $ServerName `
-Datastore $Datastore `
-NumCPU $vCPU `
-MemoryGB $vRAM`
-DiskGB $DiskSize `
-NetworkName $PortGroup `
-DiskStorageFormat $CDROM_ISO `
-GuestID $GuestType | Out-Null
}
END
{
}
}
This is expected behavior. The begin {} block runs before any pipeline objects are encountered, so you have no access to any parameters that come in through the pipeline in your begin block.
The process {} block is run once for each item, so the code you have in the begin block really needs to be put there (because it's specific to a single VM).
Let's say I have a function like:
function Authenticate
{
param
(
[ValidateSet('WindowsAuthentication','UsernameAndPassword')][string] $AuthenticationType,
[Parameter(ParameterSetName='Set1')][string] $Username,
[Parameter(ParameterSetName='Set1')][string] $Password
)
..
}
And I would like to make the parameter set to be mandatory when $AuthenticationType = 'UsernameAndPassword' but also so that it cannot be used if $AuthenticationType = 'WindowsAuthentication'.
It this even possible in PowerShell?
Using the link from Tim Ferrill's answer, I created the following function to help create dynamic parameters:
function New-DynamicParameter
{
[CmdletBinding(DefaultParameterSetName = 'Core')]
param
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][string] $Name,
[Parameter(Mandatory = $true, ParameterSetName = 'Core')][Parameter(Mandatory = $true, ParameterSetName = 'ValidateSet')][type] $Type,
[Parameter(Mandatory = $false)][string] $ParameterSetName = '__AllParameterSets',
[Parameter(Mandatory = $false)][bool] $Mandatory = $false,
[Parameter(Mandatory = $false)][int] $Position,
[Parameter(Mandatory = $false)][bool] $ValueFromPipelineByPropertyName = $false,
[Parameter(Mandatory = $false)][string] $HelpMessage,
[Parameter(Mandatory = $true, ParameterSetName = 'ValidateSet')][string[]] $ValidateSet,
[Parameter(Mandatory = $false, ParameterSetName = 'ValidateSet')][bool] $IgnoreCase = $true
)
process
{
# Define Parameter Attributes
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.ParameterSetName = $ParameterSetName
$ParameterAttribute.Mandatory = $Mandatory
$ParameterAttribute.Position = $Position
$ParameterAttribute.ValueFromPipelineByPropertyName = $ValueFromPipelineByPropertyName
$ParameterAttribute.HelpMessage = $HelpMessage
# Define Parameter Validation Options if ValidateSet set was used
if ($PSCmdlet.ParameterSetName -eq 'ValidateSet')
{
$ParameterValidateSet = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $ValidateSet -Strict (!$IgnoreCase)
}
# Add Parameter Attributes and ValidateSet to an Attribute Collection
$AttributeCollection = New-Object Collections.ObjectModel.Collection[System.Attribute]
$AttributeCollection.Add($ParameterAttribute)
$AttributeCollection.Add($ParameterValidateSet)
# Add parameter to parameter list
$Parameter = New-Object System.Management.Automation.RuntimeDefinedParameter -ArgumentList #($Name, $Type, $AttributeCollection)
# Expose parameter to the namespace
$ParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$ParameterDictionary.Add($Name, $Parameter)
return $ParameterDictionary
}
}
And solved my particular problem in the following way:
function Authenticate
{
param
(
[ValidateSet('WindowsAuthentication','UsernameAndPassword')][string] $AuthenticationType,
)
DynamicParam
{
if ($AuthenticationType -eq 'UsernameAndPassword')
{
New-DynamicParameter Username [string] -Mandatory $true
New-DynamicParameter Password [string] -Mandatory $true
}
}
...
}
It became unneeded to have a parameter set when using Dynamic Parameter so I removed the parameter set.
You can do this using DynamicParam. I saw a decent post on this recently here.
DynamicParam {
if ($AuthenticationType -eq 'UsernameAndPassword') {
#create ParameterAttribute Objects for the username and password
$unAttribute = New-Object System.Management.Automation.ParameterAttribute
$unAttribute.Mandatory = $true
$unAttribute.HelpMessage = "Please enter your username:"
$pwAttribute = New-Object System.Management.Automation.ParameterAttribute
$pwAttribute.Mandatory = $true
$pwAttribute.HelpMessage = "Please enter a password:"
#create an attributecollection object for the attributes we just created.
$attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
#add our custom attributes
$attributeCollection.Add($unAttribute)
$attributeCollection.Add($pwAttribute)
#add our paramater specifying the attribute collection
$unParam = New-Object System.Management.Automation.RuntimeDefinedParameter('username', [string], $attributeCollection)
$pwParam = New-Object System.Management.Automation.RuntimeDefinedParameter('password', [string], $attributeCollection)
#expose the name of our parameter
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('username', $unParam)
$paramDictionary.Add('password', $pwParam)
return $paramDictionary
}
}
Process {
$PSBoundParameters.username
$PSBoundParameters.password
}