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.
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.
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.
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.
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.
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
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)$'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With