将PowerShell变量作为cmdlet参数传递

Passing a PowerShell variable as a cmdlet parameter

我正在学习PowerShell为我的团队编写工具。我今天很忙,但是可以通过删除ForEach循环中的IF语句来简化它,因为命令之间的唯一区别是参数-replace或-remove。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Write-Host"This script removes or replaces en masse users' msds-SyncServerURL property"

DO {
    $TargetSSU=Read-Host"`nWhat msds-SyncServerURL do you want to replace or remove?"
    $UsersToBeFixed=Get-ADUser -Filter {msds-SyncServerURL -like $TargetSSU} -Properties ('name','samaccountname','msDS-SyncServerURL')
    IF ($UsersToBeFixed -eq $null) {
        Write-Host"`nNo users appear to have $TargetSSU as their msds-SyncServerURL value. Please try again."
    }
} UNTIL ($UsersToBeFixed -ne $null)

Write-Host"`n`nThe following users have $TargetSSU as their msds-SyncServerURL value:"
$UsersToBeFixed |select name,samaccountname,msds-syncserverurl|ft

DO {
    $Action=Read-Host"Do you want to [R]emove or [U]pdate $TargetSSU?"
} Until (($Action -eq"R") -or ($Action -eq"U"))    
    IF ($Action -eq"U") {
        DO {
            $NewSyncServer=Read-Host"`nEnter the new Sync Server's hostname (not the URL)"
            Write-Host"`nChecking to see if $NewSyncServer has a userfile share..."
            $VerifySyncServer=Test-Path \\\\$NewSyncServer\\userfiles
            IF ($VerifySyncServer -eq $false) {
                Write-host"`n$NewSyncServer does not appear to be a valid Sync Server hostname. Please try again."
            }
        } UNTIL ($VerifySyncServer -eq $true)
        $TargetSSU="https://$NewSyncServer.ourdomain.com"
    }

ForEach ($userToBeFixed in $UsersToBeFixed) {
    Write-Host"`nFixing" ($usertobefixed).name
    IF ($Action -eq"R") {
        Set-ADObject -identity $userToBeFixed -remove @{"msDS-SyncServerUrl" = $TargetSSU}
    }
    IF ($Action -eq"U") {
        Set-ADObject -identity $userToBeFixed -replace @{"msDS-SyncServerUrl" = $TargetSSU}
    }
}

Write-Host"`nHere is the result of the operation:"
foreach ($userToBeFixed in $UsersToBeFixed) {
    Get-ADUser -Identity $userToBeFixed -Properties ('name','samaccountname','msDS-SyncServerURL')|select name,samaccountname,msds-syncserverurl
}

最初,我进行了以下切换,尝试使用各种引号,甚至{$ action =" replace"}排列:

1
2
3
4
5
Switch($action)
{
    R {'remove'}
    U {'replace'}
}

我还在ForEach循环中尝试了Invoke-Expression:

1
2
$CMD="Set-ADObject -identity $userToBeFixed -$action @{`"msDS-SyncServerUrl`" = $TargetSSU}"
Invoke-Expression -Command $CMD

Set-ADObject cmdlet总是会失败,通常会抱怨Set-ADObject无法找到接受参数的位置参数,如" -remove"," System.Object []"或" System.Collections.Hashtable" 。

我将问题隔离到Set-ADObject,而不喜欢$ action变量用作参数。如果我将-$ action替换为-replace或-remove,则它将起作用(如上述代码段所示)。

我意识到这是一个小问题,但是似乎没有理由让我拥有这样的冗余代码感到困扰。我很想学习如何解决此问题。

另外,无关的,我还没有真正找到一种更好的方法来做到这一点:

1
Until (($Action -eq"R") -or ($Action -eq"U"))

我已经搜索并尝试了其他解决方案,例如:

1
Until ($Action -eq @{"R" -or"U"})

但似乎无法合并对多个条件的评估。这使我感到困扰,但没有我在这里遇到的主要问题那么严重。

请对我放松。我对这件事一无所知。如果有人看到其他问题,我可以改善,请告诉我。我想学习这个。

谢谢。


您可以通过喷溅解决此问题。

例如:

1
2
3
$action = 'Recurse'
$params = @{ $action = $true }
Get-ChildItem @params

该示例在功能上等同于Get-ChildItem -Recurse


Persistent13的有用答案显示了如何使用散列通过哈希表动态传递参数。

至:

Also, unrelated, I haven't really found a better way to do this:

1
  Until (($Action -eq"R") -or ($Action -eq"U"))

PowerShell提供了数组包含运算符:-contains(PSv1,在LHS上的数组)和-in(PSv3,在RHS上的数组):

1
2
3
4
5
# PSv3+
$Action -in 'R', 'U'

# Equivalent, PSv1+
'R', 'U' -contains $Action

两种形式都将标量操作数与数组操作数的每个元素进行比较(使用-eq逻辑),并在找到第一个匹配项(如果有)后立即返回$True

另一种选择是将-match运算符与正则表达式配合使用:

1
$Action -match '^(R|U)$'

Persistent13对如何使用散列需要哈希表的参数的解释就可以解决问题。

mklement0的简化比较解决方案有助于清除那部分代码。


我喜欢将所有不涉及脚本逻辑(即函数)的内容移至与主PowerShell脚本不同的文件中。我的大多数脚本都遵循以下结构:

Edit-SyncServerUrl.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#requires -Version 5.1

<#
    Company header
#>


<#
.SYNOPSIS
Comment-based help
#>

[CmdletBinding(SupportsShouldProcess)]
Param([String]$Option)

."$PSScriptRoot\\scriptfunctions.ps1"

<# .. variables .. #>

Switch -Regex ($Option)
{
    '^rem' {Edit-SyncServerURL -Remove}
    '^rep' {Edit-SyncServerURL -Replace}
    Default {Write-Host"Invalid option passed: $Option"}
}

在您的特定示例中,我会将提示设置等的所有逻辑带入一个函数,该函数根据传递给它的参数来选择执行路径。你可以做

1
2
3
4
5
6
7
8
9
10
Param(
    [ValidateScript({$_ -match '^(u|r)'})]
    [String]$Option=(Read-Host -Prompt 'Would you like to (r)emove or (u)pdate a target msds-SyncServerUrl?'
)

Switch -Regex ($Option)
{
    '^r' { <# Logic #> }
    '^u' { <# Logic #> }
}