Unable to evaluate against a specific value - powershell

I am having trouble thinking of the correct logic on how to go about this. I have a function that accepts two types of switches:
-Add
-Remove
Function Test-Bool {
Param (
# Input Parameters
[Parameter(Mandatory = $false,
HelpMessage='Enter. Workflow. Name.')]
[Alias('OMB','MailBox')]
[string]$Workflow,
[Parameter(Mandatory = $false)]
[Alias('EDIPI','DisplayName')]
[string[]]$UserName
)
DynamicParam {
if ($Workflow -ne $null -and $UserName -ne $null) {
$parameterAttribute = [System.Management.Automation.ParameterAttribute]#{
ParameterSetName = "AddingMembers"
Mandatory = $false
}
$attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$attributeCollection.Add($parameterAttribute)
$dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new(
'Add', [switch], $attributeCollection
)
$paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
$paramDictionary.Add('Add', $dynParam1)
$parameterAttribute1 = [System.Management.Automation.ParameterAttribute]#{
ParameterSetName = "RemovingMembers"
Mandatory = $false
}
$attributeCollection1 = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$attributeCollection1.Add($parameterAttribute1)
$dynParam11 = [System.Management.Automation.RuntimeDefinedParameter]::new(
'Remove', [switch], $attributeCollection1
)
$paramDictionary.Add('Remove', $dynParam11)
return $paramDictionary
}
}
Begin {
$ABool = {
'Add Block'
$Bool = Read-Host -Prompt 'Adding or Removing Member(s)'
if ($Bool.ToLower() -like 'a*') {
$true
}
else {
Break;
}
}
$RBool = {
'Remove Block'
$Bool = Read-Host -Prompt 'Adding or Removing Member(s)'
if ($Bool.ToLower() -like 'r*') {
$true
}
else {
Break;
}
}
if ($PSBoundParameters['Add'].IsPresent) { [bool]$Add = $true }
elseif ($PSBoundParameters['Remove'].IsPresent) { [bool]$Remove = $true }
elseif (-not$PSBoundParameters['Add'].IsPresent) { $Add = & $ABool }
elseif (-not$PSBoundParameters['Remove'].IsPresent) { $Remove = & $RBool }
}
Process {
if ($Add) {
"Add was selected"
}
if ($Remove) {
"Remove was selected"
}
}
}
I can run it several ways:
Test-Bool -Workflow spg -UserName a -Add - works
Test-Bool -Workflow spg -UserName a -Remove - works
Test-Bool -Workflow spg
Should ask to remove or add: Typing Add, works.
Typing Remove, does not work.
Which makes sense, because elseif (-not$PSBoundParameters['Add'].IsPresent) { $Add = & $ABool } is evaluated first and when asked, Remove is typed in which throws it into the else block making it Break/Exit.
QUESTION:
What can I change to make $Remove be evaluated to true and vise-versa when Add is typed (to make $Add True)?
I honestly confused myself so much that I just decided to post here instead of attempting to figure this out on my own.

Use Parameter Sets instead:
Function Test-Bool {
[CmdletBinding(DefaultParameterSetName = 'Undecided')]
Param (
# Input Parameters
[Parameter(Mandatory = $false,
HelpMessage='Enter. Workflow. Name.')]
[Alias('OMB','MailBox')]
[string]$Workflow,
[Parameter(Mandatory = $false)]
[Alias('EDIPI','DisplayName')]
[string[]]$UserName,
[Parameter(Mandatory = $true, ParameterSetName = 'Add')]
[switch]$Add,
[Parameter(Mandatory = $true, ParameterSetName = 'Remove')]
[switch]$Remove
)
begin {
$action = if($PSCmdlet.ParameterSetName -eq 'Undecided'){
$answer = Read-Host -Prompt 'Adding or Removing Member(s)?'
if($answer -like 'a*'){
'Add'
}
elseif($answer -like 'r*') {
'Remove'
}
else {
throw 'Invalid option provided'
}
}
else {
$PSCmdlet.ParameterSetName
}
# $action now contains either 'Add' or 'Remove'
$actionBlock = #{
'Add' = { <# code to add user to workflow #> }
'Remove' = { <# code to remove user from workflow #> }
}[$action]
}
process {
# if/else statements no longer needed, $actionBlock contains the correct scriptblock
. $actionBlock
}
}
If a user doesn't specify either switch, the parameter set name will be Undecided, and the user will be prompted in the begin block - otherwise we simply use the parameter set name associated with the specified switch.
Since the two switch parameters belong to separate parameter sets, the user can no longer pick both.

Related

Does a DynamicParameter Switch not read like a Static Parameter [switch]?

Hopefully the Title is clear enough but, I am having some trouble understanding on how to evaluate against a DynamicParameter Switch, compared to a Static (type casted) switch.
In the following code block, there are 2 switches that become available only if the other 2 parameters are not null, and/or, are not empty:
Add
Remove
Function Test-DynamParam {
Param (
# Input Parameters
[Parameter(Mandatory = $false,
HelpMessage='Enter. Workflow. Name.')]
[Alias('OMB','MailBox')]
[string]$Workflow,
[Parameter(Mandatory = $false)]
[Alias('EDIPI','DisplayName')]
[string[]]$UserName
)
DynamicParam {
if ($Workflow -ne $null -and $UserName -ne $null) {
$parameterAttribute = [System.Management.Automation.ParameterAttribute]#{
ParameterSetName = "AddingMembers"
Mandatory = $false
}
$attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$attributeCollection.Add($parameterAttribute)
$dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new(
'Add', [switch], $attributeCollection
)
$paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
$paramDictionary.Add('Add', $dynParam1)
$parameterAttribute1 = [System.Management.Automation.ParameterAttribute]#{
ParameterSetName = "RemovingMembers"
Mandatory = $false
}
$attributeCollection1 = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$attributeCollection1.Add($parameterAttribute1)
$dynParam11 = [System.Management.Automation.RuntimeDefinedParameter]::new(
'Remove', [switch], $attributeCollection1
)
$paramDictionary.Add('Remove', $dynParam11)
return $paramDictionary
}
}
Process {
$Add.IsPresent
}
}
Running:
Test-DynamParam -Workflow 'd' -UserName 'a' -Add
returns empty.
Unfortunately, $Add.IsPresent is not evaluated to any boolean value regardless if the switch is present or not. Yet in this function it is (which makes sense):
Function Test-StaticParam {
Param (
[switch]$Add
)
$Add.IsPresent
}
Running:
Test-StaticParam -Add
returns True.
Question
How can I evaluate against dynamic parameter chosen?
Use the $PSBoundParameters automatic variable:
Process {
$PSBoundParameters['Add'].IsPresent
}

A more compact / elegant way to navigate complex / nested hashtables with Invoke-Expression? [duplicate]

This question already has answers here:
Access PSObject property indirectly with variable
(3 answers)
Editing/Accessing Powershell objects using variables with dot notation
(3 answers)
Closed 2 years ago.
I'm working on a function to acess/modify nested hashtables via string input of keys hierarchy like so:
putH() $hashtable "key.key.key...etc." "new value"
Given:
$c = #{
k1 = #{
k1_1 = #{
k1_1_1 = #{ key = "QQQQQ"}
}
}
}
so far i've come up with this function for modifying values:
function putH ($h,$hKEYs,$nVAL){
if ($hKEYs.count -eq 1) {
$bID = $hKEYs #match the last remaining obj in $hkeys
}
else {
$bID = $hKEYs[0] #match the first obj in $hekys
}
foreach ($tk in $h.keys){
if ($tk -eq $bID){
if ($hKEYs.count -eq 1){ #reached the last obj in $hkeys so modify
$h.$tk = $nVAL
break
}
else {
$trash,$hKEYs = $hKEYs #take out the first obj in $hkeys
$h.$tk = putH $h.$tk $hKEYs $nVAL #call the function again for the nested hashtale
break
}
}
}
return $h
}
and this function for getting values :
function getH ($h,$hKEYs){
if ($hKEYs.count -eq 1) {
$bID = $hKEYs
}
else {
$bID = $hKEYs[0]
}
foreach ($tk in $h.keys){
if ($tk -eq $bID){
if ($hKEYs.count -eq 1){
$h = $h.$tk
break
}
else {
$trash,$hKEYs = $hKEYs
$h = getH $h.$tk $hKEYs
break
}
}
}
return $h
}
that i use like so:
$s = "k1.k_1.k1_1_1" #custom future input
$s = $s.split(".")
putH $c ($s) "NEW_QQQQQ"
$getval = getH $c ($s)
My question:
is there a more elegant way to achieve the function's results...say with invoke-expression?
i've tried invoke-expression - but can't access the hassstables trough it (no matter the combinations, nested quotes)
$s = "k1.k_1.k1_1_1" #custom future input
iex "$c.$s"
returns
System.Collections.Hashtable.k1.k_1.k1_1_1
Don't use Invoke-Expression
I'll answer your question at the bottom, but I feel obliged to point out that calling Invoke-Expression here is both dangerous and, more importantly, unnecessary.
You can resolve the whole chain of nested member references by simply splitting the "path" into its individual parts ('A.B.C' -> #('A', 'B', 'C')) and then dereferencing them one-by-one (you don't even need recursion for this!):
function Resolve-MemberChain
{
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[psobject[]]$InputObject,
[Parameter(Mandatory = $true, Position = 0)]
[string[]]$MemberPath,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$Delimiter = '.'
)
begin {
$MemberPath = $MemberPath.Split([string[]]#($Delimiter))
}
process {
foreach($o in $InputObject){
foreach($m in $MemberPath){
$o = $o.$m
}
$o
}
}
}
Now you can solve your problem without iex:
$ht = #{
A = #{
B = #{
C = "Here's the value!"
}
}
}
$ht |Resolve-MemberChain 'A.B.C' -Delimiter '.'
You can use the same approach to update nested member values - simply stop at the last step and then assign to $parent.$lastMember:
function Set-NestedMemberValue
{
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[psobject[]]$InputObject,
[Parameter(Mandatory = $true, Position = 0)]
[string[]]$MemberPath,
[Parameter(Mandatory = $true, position = 1)]
$Value,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$Delimiter = '.'
)
begin {
$MemberPath = $MemberPath.Split([string[]]#($Delimiter))
$leaf = $MemberPath |Select -Last 1
$MemberPath = $MemberPath |select -SkipLast 1
}
process {
foreach($o in $InputObject){
foreach($m in $MemberPath){
$o = $o.$m
}
$o.$leaf = $Value
}
}
}
And in action:
PS ~> $ht.A.B.C
Here's the value!
PS ~> $ht |Set-NestedMemberValue 'A.B.C' 'New Value!'
PS ~> $ht.A.B.C
New Value!
Why isn't your current approach working?
The problem you're facing with your current implementation is that the $c in $c.$s gets expanded as soon as the string literal "$c.$s" is evaluated - to avoid that, simply escape the first $:
iex "`$c.$s"

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
}
}
}

Application runs in Windows 7 but not in Windows 10

I have created an app for back up and restore of computers. I also allows modification of ADObjects through the use of custom Profile.ps1 file. The app runs fine in the ISE with no errors and works properly no errors in Windows 7. However, when I try to run it in a newly imaged Windows 10 machine I get "Property Can Not Be Found" errors on all my object properties.
The thing is I can read all the properties when I fill comboboxes fine. The error only occurs when the form is submitted. I will attach 1 of the forms I am having a problem with. Again it runs fine in Windows 7, but not Windows 10.
Could this be a problem with Microsoft updates?
Also, yes, I am setting Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted.
Error message:
The property 'company' cannot be found on this object. Verify that the
property exist and can be set.
+ $CO.company = $company
+ Categoryinfo :InvalidOperation: (:) [] RuntimeExeption
Code:
. \\iotsdsp01pw\installs$\MoveToOU\PcDeployment\Profile.ps1
#region Validation Functions
function Validate-IsEmail ([string]$Email) {
return $Email -match "^(?("")("".+?""#)|(([0-9a-zA-Z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-zA-Z])#))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,6}))$"
}
function Validate-IsURL ([string]$Url) {
if ($Url -eq $null) {
return $false
}
return $Url -match "^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_]*)?$"
}
function Validate-IsName ([string]$Name, [int]$MaxLength) {
if ($MaxLength -eq $null -or $MaxLength -le 0) {
#Set default length to 40
$MaxLength = 40
}
return $Name -match "^[a-zA-Z''-'\s]{1,$MaxLength}$"
}
function Validate-IsIP ([string]$IP) {
return $IP -match "\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"
}
function Validate-IsEmptyTrim ([string]$Text) {
if ($text -eq $null -or $text.Trim().Length -eq 0) {
return $true
}
return $false
}
function Validate-IsEmpty ([string]$Text) {
return [string]::IsNullOrEmpty($Text)
}
function Validate-IsDate ([string]$Date) {
return [DateTime]::TryParse($Date, [ref](New-Object System.DateTime))
}
#endregion
$No_Load = {
$NewForm.Close()
#Initialize variables
$dateTime = Get-Date -Format G
$userName = (Get-WmiObject -Class Win32_ComputerSystem -Property UserName).UserName
$computerName = $env:computername
#Varables for display
$distinguishedName = (Get-dn computer cn $computerName)
$computerObject = (Get-ADObject $distinguishedName)
$organizationalUnit = (Get-ADObject "OU=Agencies, DC=state, DC=in, DC=us")
#Initialize Form Controls
$lblUserNameNewNo.Text = $userName
$lblComputerNameNewNo.Text = $computerName
$lblPhysicalLocationNewNo.Text = $computerObject.location
$txtBillingCodeNewNo.Text = $computerObject.departmentNumber
$comboboxAccountTypeNewNo.Text = $computerObject.extensionAttribute15
$comboboxOrganizationalUnitNewNo.Text = $computerObject.company
Load-ComboBox -ComboBox $comboboxOrganizationalUnitNewNo ($organizationalUnit.children | %{ $_.OU })
}
#region Control Helper Functions
function Load-ComboBox {
Param (
[ValidateNotNull()]
[Parameter(Mandatory = $true)]
[System.Windows.Forms.ComboBox]$ComboBox,
[ValidateNotNull()]
[Parameter(Mandatory = $true)]
$Items,
[Parameter(Mandatory = $false)]
[string]$DisplayMember,
[switch]$Append
)
if (-not $Append) {
$ComboBox.Items.Clear()
}
if ($Items -is [Object[]]) {
$ComboBox.Items.AddRange($Items)
} elseif ($Items -is [Array]) {
$ComboBox.BeginUpdate()
foreach ($obj in $Items) {
$ComboBox.Items.Add($obj)
}
$ComboBox.EndUpdate()
} else {
$ComboBox.Items.Add($Items)
}
$ComboBox.DisplayMember = $DisplayMember
}
#Validation
function ParameterValidate {
Param (
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[ValidateLength(1, 10)]
[String]$Text
)
return $true
}
$comboboxOrganizationalUnitNewNo_Validating = [System.ComponentModel.CancelEventHandler]{
#Check if the Name field is empty
$result = Validate-IsEmptyTrim $comboboxOrganizationalUnitNewNo.Text
if ($result -eq $true) {
#Mark a failure only if the Validation failed
$script:ValidationFailed = $true
#Display an error message
$errorprovider1.SetError($comboboxOrganizationalUnitNewNo, "Please select agency.");
} else {
#Clear the error message
$errorprovider1.SetError($comboboxOrganizationalUnitNewNo, "");
}
}
$txtBillingCodeNewNo_Validating = [System.ComponentModel.CancelEventHandler]{
#Check if the Name field is empty
$result = Validate-IsEmptyTrim $txtBillingCodeNewNo.Text
if ($result -eq $true) {
#Mark a failure only if the Validation failed
$script:ValidationFailed = $true
#Display an error message
$errorprovider1.SetError($txtBillingCodeNewNo, "Please enter billing code.");
} else {
#Clear the error message
$errorprovider1.SetError($txtBillingCodeNewNo, "");
}
}
$comboboxAccountTypeNewNo_Validating = [System.ComponentModel.CancelEventHandler]{
$result = Validate-IsEmptyTrim $comboboxAccountTypeNewNo.Text
if ($result -eq $true) {
#Mark a failure only if the Validation failed
$script:ValidationFailed = $true
#Display an error message
$errorprovider1.SetError($comboboxAccountTypeNewNo, "Please enter agency type.");
} else {
#Clear the error message
$errorprovider1.SetError($comboboxAccountTypeNewNo, "");
}
}
$control_Validated = {
#Pass the calling control and clear error message
$errorprovider1.SetError($this, "");
}
$No_FormClosing = [System.Windows.Forms.FormClosingEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.FormClosingEventArgs]
#Validate only on OK Button
if ($No.DialogResult -eq "OK") {
#Init the Validation Failed Variable
$script:ValidationFailed = $false
#Validate the Child Control and Cancel if any fail
$No.ValidateChildren()
#Cancel if Validation Failed
$_.Cancel = $script:ValidationFailed
}
}
#Events
$buttonColor_Click = {
#TODO: Place custom script here
$colordialog1.ShowDialog()
}
$linklblViewListNewNo_LinkClicked = [System.Windows.Forms.LinkLabelLinkClickedEventHandler]{
Start-Process "http://billingcodes.iot/"
}
$btnSubmitNewNo_Click = {
#TODO: Place custom script here
$company = $comboboxOrganizationalUnitNewNo.Text
$departmentNumber = $txtBillingCodeNewNo.Text
$accountType = $comboboxAccountTypeNewNo.Text
if ($accountType -eq "Seat") {
$accountType = " "
}
#Varables for Set-ADObject
$computerObject.company = $company
$computerObject.departmentNumber = $departmentNumber
$computerObject.extensionAttribute15 = $accountType
try {
$computerObject.SetInfo()
[Environment]::Exit(1)
} catch {
$labelDialogRedNewNo.Text = "AD computer object not found"
}
}
This is your culprit (from what I can see):
$No_Load = {
...
$computerObject = (Get-ADObject $distinguishedName)
...
}
...
$btnSubmitNewNo_Click = {
...
$computerObject.company = $company
...
}
You assign a computer object to the variable $computerObject in one scriptblock, and try to change one of its properties in another scriptblock. However, to be able to share a variable between scriptblocks you need to make it a global variable, otherwise you have two separate local variables that know nothing about each other.
$No_Load = {
...
$global:computerObject = Get-ADObject $distinguishedName
...
}
...
$btnSubmitNewNo_Click = {
...
$global:computerObject.company = $company
...
}
BTW, I doubt that this ever worked in Windows 7, since it failed with the same error on my Windows 7 test box.

How to generate dynamic parameters for script cmdlet by reading an msbuild project?

I'm attempting to create a script cmdlet with dynamic parameters for each of the targets in an MSBuild project file. It's working with one minor annoyance - it only autocompletes the static parameters until I type the first character(s) of a target parameter.
My script with the cmdlet "Make-Project" follows.
What would cause DynamicParam to not return parameters unless the first part of the parameter is entered?
Set-Alias mk Make-Project
function Make-Project
{
[CmdletBinding(DefaultParameterSetName="Build")]
PARAM(
[Parameter(ParameterSetName="Build")]
[Switch]$Build,
[Parameter(ParameterSetName="Clean")]
[Switch]$Clean,
[Parameter(ParameterSetName="Rebuild")]
[Switch]$Rebuild,
[ValidateSet( "q","quiet", "m","minimal", "n","normal", "d","detailed", "diag","diagnostic" )]
[string]$BuildVerbosity,
[Switch]$CertificationBuild,
[Switch]$BuildDebug
)
PROCESS
{
# Defaults
$certBuild = ""
$target = "Usage"
$buildTarget = "Build"
$verbosity = "minimal"
$configuration = "release"
if ( [System.Convert]::ToBoolean( $env:project_build_debug ) )
{ $configuration = "debug" }
foreach( $paramName in $MyInvocation.BoundParameters.Keys )
{
switch -RegEx ( $paramName )
{
"(?i)CertificationBuild" { $certBuild = "cert" }
"(?i)^(Build|Clean|Rebuild)$" { $buildTarget = $paramName }
"(?i)^BuildVerbosity$" { $verbosity = $MyInvocation.BoundParameters[ $paramName ] }
"(?i)^BuildDebug$" { $configuration = "debug" }
default { $target = $paramName }
}
}
$msbuildexe = Get-MSBuildExe
if ( $msbuildexe.Contains( "v4.0" ) )
{ $cmd = "$msbuildexe /v:$verbosity $certBuild /m /property:Configuration=$configuration /property:BuildTarget=$buildTarget /target:$target /property:CLR4=1 Project.proj" }
else
{ $cmd = "$msbuildexe /v:$verbosity $certBuild /m /property:Configuration=$configuration /property:BuildTarget=$buildTarget /target:$target /tv:3.5 Project.proj" }
Write-Host $cmd
Invoke-Expression $cmd
}
DynamicParam
{
$projFile = '.\Project.Proj'
$projXml = [xml]( Get-Content $projFile )
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$projXml.Project.Target | % {
$paramName = $_.Name
$attribute = New-Object System.Management.Automation.ParameterAttribute
$attribute.ParameterSetName = "__AllParameterSets"
$attribute.Mandatory = $false
$attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attribute)
$param = New-Object System.Management.Automation.RuntimeDefinedParameter( $paramName, [Switch], $attributeCollection )
$paramDictionary.Add( $paramName, $param )
}
return $paramDictionary
}
}
function Get-MSBuildExe
{
$bitness = ""
if ( $env:PROCESSOR_ARCHITECTURE -eq "AMD64" )
{ $bitness = "64" }
$msbuildexe = "$env:SystemRoot\Microsoft.NET\Framework${bitness}\v4.0.30319\MSBuild.exe"
if ( -not (Test-Path $msbuildexe) )
{ $msbuildexe = "$env:SystemRoot\Microsoft.NET\Framework${bitness}\v3.5\MSBuild.exe" }
$msbuildexe
}
I would suggest that you not add a bunch of switch parameters. Instead, add one dynamic parameter named "Target" and add a ValidateSet attribute to it with the list of targets.
This is a helpful article.
http://robertrobelo.wordpress.com/2010/02/12/add-parameter-validation-attributes-to-dynamic-parameters/