Custom function, value from pipeline - powershell

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]

Related

PowerShell getting logs from Jobs

I am using a PowerShell module called Logging. I use other Jobs to do some work, but unforunately the logs that I write in them are not received by the command Receive-Job.
function Get-Topic {
[CmdletBinding(DefaultParameterSetName = "Normal")]
[OutputType(ParameterSetName = "Normal")]
[OutputType(ParameterSetName = "AsJob")]
param (
[Parameter(Mandatory = $true, ParameterSetName = "Normal")]
[Parameter(Mandatory = $true, ParameterSetName = "AsJob")]
[string]
$ResourceGroupName,
[Parameter(Mandatory = $true, ParameterSetName = "Normal")]
[Parameter(Mandatory = $true, ParameterSetName = "AsJob")]
[string]
$Namespace,
[Parameter(Mandatory = $true, ParameterSetName = "Normal")]
[Parameter(Mandatory = $true, ParameterSetName = "AsJob")]
[string]
$Topic,
[Parameter(Mandatory = $true, ParameterSetName = "AsJob")]
[switch]
$AsJob
)
if ($AsJob) {
$PSBoundParameters.Remove('AsJob') | Out-Null
return Start-ThreadJob -ScriptBlock {
param(
[string]$myFunction,
[System.Collections.IDictionary]$argTable,
[System.Collections.Concurrent.ConcurrentDictionary[string, hashtable]] $loggingTargets
)
$loggingTargets.Keys | ForEach-Object { Add-LoggingTarget -Name $_ -Configuration $loggingTargets[$_] }
$cmd = [scriptblock]::Create($myFunction)
& $cmd #argTable
} -ArgumentList $MyInvocation.MyCommand.Definition, $PSBoundParameters, (Get-LoggingTarget)
}
$topicObj = Get-AzServiceBusTopic `
-ResourceGroupName $ResourceGroupName `
-Namespace $Namespace `
-Name $Topic
Write-Log -Message "Received topic $($topicObj.Name)" -Level INFO
return $topicObj
}
Is there any way to redirect the output to the parent powershell session? I saw that Write-Host works, but the logger with the Console target doesn't. Any workarounds for this?
The solution is the following:
return Start-ThreadJob -StreamingHost (Get-Host) -ScriptBlock {
param(
[string]$myFunction,
[System.Collections.IDictionary]$argTable,
[System.Collections.Concurrent.ConcurrentDictionary[string, hashtable]] $loggingTargets
)
$loggingTargets.Keys | ForEach-Object { Add-LoggingTarget -Name $_ -Configuration $loggingTargets[$_] }
$cmd = [scriptblock]::Create($myFunction)
& $cmd #argTable
} -ArgumentList $MyInvocation.MyCommand.Definition, $PSBoundParameters, (Get-LoggingTarget)
Thanks god the cmdlet Start-ThreadJob allows for such a functionality, basically the param -StreamingHost solved the deal

How to reuse/extend functions on Powershell?

I'm trying to develop 2 functions with Powershell. The first, will check my database status (online/offline). The second function should loop on the first function until a certain state is achieve.
function Get-DBStatus
{
<# .. removed help section for brevity .. #>
[CmdletBinding()]
[OutputType([System.Object])]
param
(
[Parameter(Mandatory = $true)]
[String]$ServerName,
[Parameter(Mandatory = $true)]
[String]$ServerUser,
[Parameter(Mandatory = $true)]
[String]$ServerPassword,
[Parameter(Mandatory = $true)]
[String]$DatabaseName,
)
try
{
$params = #{ ... }
$dbStatus = Invoke-SqlConnection #params | Where-Object {$_.Name -match $AltDBName }
}
catch
{
Write-Error -Message ('An error has occured while ...')
}
if ([String]::IsNullOrEmpty($dbStatus) -eq $false)
{
$dbStatus
}
# <<< function Get-DbStatusOnlyIf
# <<< same parameters as the function above
# <<< get the desired status as a new parameter
# <<< loop the function above until the desired status is achieved or a timeout is reached
}
I'm new to Powershell and I think I shouldn't repeat myself rewriting the same parameters from the first function into the second one since they're dependent. However, I might be wrong, thus the question.
Thank you for your assistance!
You have to rewrite this parameters on your second function and pass them through or add another paramter to your first function that will do the looping. I would go with the second solution.
Try something like that
function Get-DBStatus {
<# .. removed help section for brevity .. #>
[CmdletBinding()]
[OutputType([System.Object])]
param
(
[Parameter(Mandatory = $true)]
[String]$ServerName,
[Parameter(Mandatory = $true)]
[String]$ServerUser,
[Parameter(Mandatory = $true)]
[String]$ServerPassword,
[Parameter(Mandatory = $true)]
[String]$DatabaseName,
$WaitForStatus, #or something like that
[int]$Timeout=10
)
do {
try {
#$params = #{ ... }
$dbStatus = Invoke-SqlConnection #params | Where-Object {$_.Name -match $AltDBName }
}
catch {
Write-Error -Message ('An error has occured while ...')
return
}
if ([String]::IsNullOrEmpty($dbStatus) -eq $false) {
if ($WaitForStatus){
if ($dbStatus -eq $WaitForStatus) {
$dbStatus
$EndLoop = $true
}
else {
Write-Host -NoNewline "." #only for test
Start-Sleep -Seconds 1
$Timeout -= 1
}
}
else{
$dbStatus
$EndLoop = $true
}
}
}
until ($EndLoop -or $Timeout -eq 0)
}
or with recursion
function Get-DBStatus {
<# .. removed help section for brevity .. #>
[CmdletBinding()]
[OutputType([System.Object])]
param
(
[Parameter(Mandatory = $true)]
[String]$ServerName,
[Parameter(Mandatory = $true)]
[String]$ServerUser,
[Parameter(Mandatory = $true)]
[String]$ServerPassword,
[Parameter(Mandatory = $true)]
[String]$DatabaseName,
$WaitForStatus, #or something like that
[int]$timeout = 3
)
if ($WaitForStatus) {
$start = Get-Date
while (((get-date) - $start).TotalSeconds -lt $timeout) {
$res = Get-DBStatus -ServerName $ServerName -ServerUser $ServerUser -ServerPassword $ServerPassword -DatabaseName $DatabaseName
if ($WaitForStatus -eq $res) {
return $res
}
Start-Sleep -Seconds 1
}
}
else {
try {
$params = #{ ... }
$dbStatus = Invoke-SqlConnection #params | Where-Object {$_.Name -match $AltDBName }
}
catch {
Write-Error -Message ('An error has occured while ...')
}
if ([String]::IsNullOrEmpty($dbStatus) -eq $false) {
$dbStatus
}
}
}

Edited post, but still listed as duplicate [duplicate]

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).

Powershell Object not being piped through to Functions

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).

Can I make a parameter set depend on the value of another parameter?

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
}