<# .SYNOPSIS Adds (or removes) domain groups in the local Administrators group on remote computers. .DESCRIPTION Uses PowerShell remoting to run Add-LocalGroupMember or Remove-LocalGroupMember on each computer. The built-in Administrators group is found by its well-known SID (S-1-5-32-544), so it works on non-English Windows too. Each computer and member gets its own result row, and one unreachable machine doesn't stop the rest. Supports -WhatIf. .PARAMETER ComputerName Computers to change. Accepts pipeline input, so Get-Content .\computers.txt | works. .PARAMETER Member Domain groups (or users) to add or remove, e.g. CONTOSO\Tier2-Workstation-Admins. .PARAMETER Remove Remove the members instead of adding them. .PARAMETER Credential Alternate credentials for the remoting connection. .EXAMPLE Get-Content .\computers.txt | .\Set-LocalAdminMember.ps1 -Member 'CONTOSO\Tier2-Workstation-Admins' -WhatIf .EXAMPLE .\Set-LocalAdminMember.ps1 -ComputerName PC-0142, PC-0187 -Member 'CONTOSO\Old-Helpdesk' -Remove #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('Name', 'DNSHostName')][AllowEmptyString()][string[]]$ComputerName, [Parameter(Mandatory)][ValidatePattern('^[^\\]+\\[^\\]+$')][string[]]$Member, [switch]$Remove, [pscredential]$Credential ) begin { $verb = if ($Remove) { 'Remove' } else { 'Add' } $prep = if ($Remove) { 'from' } else { 'to' } # Runs on the target computer. Returns one result per member. $remoteBlock = { param([string[]]$Members, [bool]$Remove) $adminsSid = 'S-1-5-32-544' foreach ($m in $Members) { try { if ($Remove) { Remove-LocalGroupMember -SID $adminsSid -Member $m -ErrorAction Stop } else { Add-LocalGroupMember -SID $adminsSid -Member $m -ErrorAction Stop } [pscustomobject]@{ Member = $m; Result = 'Done'; Detail = '' } } catch [Microsoft.PowerShell.Commands.MemberExistsException] { [pscustomobject]@{ Member = $m; Result = 'Skipped'; Detail = 'Already a member' } } catch [Microsoft.PowerShell.Commands.MemberNotFoundException] { [pscustomobject]@{ Member = $m; Result = 'Skipped'; Detail = 'Not a member' } } catch { [pscustomobject]@{ Member = $m; Result = 'Failed'; Detail = $_.Exception.Message } } } } } process { foreach ($computer in $ComputerName) { $computer = $computer.Trim() if (-not $computer) { continue } $approved = @($Member | Where-Object { $PSCmdlet.ShouldProcess($computer, "$verb $_ $prep local Administrators") }) foreach ($m in $Member | Where-Object { $approved -notcontains $_ }) { [pscustomobject]@{ ComputerName = $computer; Action = $verb; Member = $m; Result = 'WhatIf'; Detail = '' } } if (-not $approved) { continue } $invoke = @{ ComputerName = $computer; ScriptBlock = $remoteBlock; ArgumentList = @($approved, [bool]$Remove); ErrorAction = 'Stop' } if ($Credential) { $invoke.Credential = $Credential } try { foreach ($r in Invoke-Command @invoke) { [pscustomobject]@{ ComputerName = $computer; Action = $verb; Member = $r.Member; Result = $r.Result; Detail = $r.Detail } } } catch { # Couldn't reach the machine at all: report every member as failed. foreach ($m in $approved) { [pscustomobject]@{ ComputerName = $computer; Action = $verb; Member = $m; Result = 'Failed'; Detail = $_.Exception.Message } } } } }