I currently am writing a Powershell script that remotely removes users from a local admin group on a list of servers. The CSV headers are Computer and Name. For each entry of user (name), matches the server (computer).
Ex.
Computer,Name
Server1,User1
Server1,User2
Server2,User1
Script:
$List = Import-CSV C:\temp\LocalAdmin.CSV
$user = $List.Name
$objGroup = $List.Computer
write-host "Removing user" $user "from server" $objGroup "local admin group:" -ForegroundColor Green
Invoke-Command -ComputerName $objGroup -ScriptBlock {Remove-LocalGroupMember -Group "Administrators" -Member $using:user }
write-host "Completed."
When the script runs, it runs through perfectly fine the first time through, but then it runs through the script line by line for how many ever lines there are causing it to attempt to remove the users multiple times. Can someone help me fix this logic? It is almost like the CSV is being read as an array vs a list. I appreciate the help!
CodePudding user response:
I would say:
$List = Import-CSV C:\temp\LocalAdmin.CSV
ForEach ($Item in $list)
{
$user = $Item.Name
$objGroup = $Item.Computer
write-host "Removing user $user from server $objGroup local admin group:" -ForegroundColor Green
Invoke-Command -ComputerName $objGroup -ScriptBlock {Remove-LocalGroupMember -Group "Administrators" -Member $user }
}
write-host "Completed."
Not tested, but this should be your starting point.
CodePudding user response:
As for what you tried:
Given that $List
contains an array of objects, with each element containing an object representing a row of the CSV file, $List.Name
and $List.Computer
similarly return arrays of property (column) values, courtesy of PowerShell's member-access enumeration
Therefore, $using:user
refers to the array of all usernames, across all servers.
While the -Member
parameter of Remove-LocalGroupMember
does accept arrays, there are two problems with your approach:
At least hypothetically you'll run the risk of deleting users you shouldn't from certain servers, or you'll run into users that don't exist on a given server (though you could ignore that with
-ErrorAction Ignore
).Since a given server name can appear multiple times in the CSV, the targeted user(s) will have already been deleted, starting with the second call to that server - this is the problem you saw.
TheStingPilot's helpful answer
provides an effective solution: loop over the objects representing the CSV rows one by one, and call Invoke-Command
for each target server, with only the username at hand.
The downside of this approach - which may or may not matter, depending on how many computers you target - is that forgo the benefits of parallel execution that you get when you pass multiple computer names to Invoke-Command
's -Computer
parameter (by default, up to 32 computers at a time are targeted in parallel; you can modify that number with -ThrottleLimit
).
To avoid multiple calls to a given server while preserving the benefits of parallel execution:
Build a hashtable from the CSV input that maps server names to user names.
Pass that hashtable to a single
Invoke-Command
call, as you tried, and let each remote computer look up the relevant usernames in it and act on them.
# Read the CSV file and create a hashtable (map) that
# maps server (computer) names to usernames to remove.
$serverUserMap = [ordered] @{}
Import-CSV C:\temp\LocalAdmin.CSV |
ForEach-Object {
[array] $serverUserMap[$_.Computer] = $serverUserMap[$_.Computer] $_.Name
}
# * $serverUserMap.Keys now contains all unique server names,
# which can be passed to -ComputerName
# * Inside the script block, accessing the hashtable with the local
# computer name as the key returns only the relevant user(s).
Invoke-Command -ComputerName $serverUserMap.Keys -ScriptBlock {
Remove-LocalGroupMember -Group "Administrators" -Member ($using:serverUserMap)[$env:COMPUTERNAME]
}