Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a PowerShell variable as a cmdlet parameter

I am learning PowerShell to write tools for my team. I was in a pinch today and got this working, but I want to streamline it by removing the IF statements in the ForEach loop, since the only difference between the commands is the parameter -replace or -remove.

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
}

I initially had the following switch in place, trying various permutations of quotation marks and even {$action="replace"}:

Switch($action)
{
    R {'remove'}
    U {'replace'}
}

I also tried Invoke-Expression in the ForEach loop:

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

Invariably, the Set-ADObject cmdlet would fail, typically complaining about Set-ADObject not being able to find positional parameters that accepts arguments like '-remove', 'System.Object[]' or 'System.Collections.Hashtable'.

I isolated the issue to the Set-ADObject not liking the $action variable being used as a parameter. If I replaced -$action with -replace or -remove it would work (as it is in the above code snippet).

I realize this is a minor issue, but it bothers me to have such redundant code for seemingly no reason. I would love to learn how to work around this.

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

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

I have searched and tried other solutions like:

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

But can't seem to consolidate the evaluation of multiple conditions. This bothers me, but not as much as my main question here.

Please go easy on me. I'm new to this whole thing. If anyone sees anything else I can improve let me know. I want to learn this.

Thanks.

like image 857
RobOfTheSkyPpl Avatar asked Sep 08 '17 17:09

RobOfTheSkyPpl


People also ask

How do you pass a variable in PowerShell?

To pass a variable to a parameter that expects a reference, you must type cast your variable as a reference. The brackets and parenthesis are BOTH required.

How do I declare a parameter in PowerShell script?

One way to use PowerShell function parameters in a script is via parameter name -- this method is called named parameters. When calling a script or function via named parameters, use the entire name of the parameter. For example, perhaps the example param block above is stored in a PowerShell script called foo. ps1.

What is param () in PowerShell?

PowerShell Parameter is an input to the Advanced Function or CmdLet and uses Parameter Attributes, Arguments, and Argument Values to further describe and limit the input so we can get the desired result from the function.

How do you Force a parameter in PowerShell?

The -Force switch is used to declare "I know what I'm doing, and I'm sure I want to do this". For example, when copying a file ( Copy-File ) the -Force parameter means: Allows the cmdlet to copy items that cannot otherwise be changed, such as copying over a read-only file or alias.


2 Answers

You can solve this with splatting.

For example:

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

The example is functionally equivalent to Get-ChildItem -Recurse

like image 129
Persistent13 Avatar answered Nov 09 '22 21:11

Persistent13


Persistent13's helpful answer shows you how to use splatting to pass parameters dynamically, via a hashtable.

As for:

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

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

PowerShell offers array containment operators: -contains (PSv1+, array on the LHS) and -in (PSv3+, array on the RHS):

# PSv3+
$Action -in 'R', 'U'

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

Both forms compare the scalar operand to every element of the array operand (using -eq logic) and return $True as soon as the first match (if any) is found.

Another option is to use the -match operator with a regular expression:

$Action -match '^(R|U)$'
like image 36
mklement0 Avatar answered Nov 09 '22 22:11

mklement0