Managing and modifying user membership within groups, whether on a local machine or in Active Directory, is a common task for IT professionals. Efficiently handling these operations can greatly enhance system administration, making processes streamlined and error-free. The power of scripting comes to the fore in this context, offering automation and precision.
Background
The provided script delves deep into the essence of IT management – allowing administrators to either add or remove users from specified groups. It is designed for versatility, functioning both within the realm of a local computer and in the broader sphere of Active Directory. As businesses and Managed Service Providers (MSPs) grow, manual user management can become arduous. Such scripts not only reduce the time spent on routine tasks but also minimize human errors.
The Script
#Requires -Version 2.0 <# .SYNOPSIS Add or remove a user to a group in Active Directory or the local computer. .DESCRIPTION Add or remove a user to a group in Active Directory or the local computer. .EXAMPLE -Group "MyGroup" -UserName "MyUser" -Action Add -IsDomainUser Adds MyUser to the group MyGroup in AD. .EXAMPLE -Group "MyGroup" -UserName "MyUser" -Action Remove -IsDomainUser Removes MyUser from the group MyGroup in AD. .EXAMPLE -Group "MyGroup" -UserName "MyUser" -Action Add Adds MyUser to the group MyGroup on the local computer. .EXAMPLE PS C:> Modify-User-Membership.ps1 -Group "MyGroup" -UserName "MyUser" -Action Remove Removes MyUser from the group MyGroup on the local computer. .OUTPUTS String[] .NOTES Minimum OS Architecture Supported: Windows 7, Windows Server 2012 This will require RSAT with the AD feature to be installed to function. Release Notes: Initial Release By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use. Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). .COMPONENT ManageUsers #> [CmdletBinding()] param ( # Specify one Group [Parameter(Mandatory = $true)] [String] $Group, # Specify one User [Parameter(Mandatory = $true)] [String] $UserName, # Add or Remove user from group [Parameter(Mandatory = $true)] [ValidateSet("Add", "Remove")] [String] $Action, # Modify a domain user's membership [Parameter(Mandatory = $false)] [Switch] $IsDomainUser ) begin { function Test-IsElevated { $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object System.Security.Principal.WindowsPrincipal($id) if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Output $true } else { Write-Output $false } } } process { if (-not (Test-IsElevated)) { Write-Error -Message "Access Denied. Please run with Administrator privileges." exit 1 } if (-not $IsDomainUser) { # Modify Local User if ($Action -like "Remove") { if ($PSVersionTable.PSVersion.Major -lt 3) { # Connect to localhost try { $ADSI = [ADSI]("WinNT://$env:COMPUTERNAME") } catch { Write-Error -Message "Failed to connect to $env:COMPUTERNAME via ADSI object" exit 1 } # Find the group try { $ASDIGroup = $ADSI.Children.Find($Group, 'group') } catch { Write-Error -Message "Failed to find $Group via ADSI object" exit 1 } # Remove the user from the group try { $ASDIGroup.Remove(("WinNT://$env:COMPUTERNAME/$UserName")) } catch { Write-Error -Message "Failed to remove User $UserName from Group $Group" exit 529 # ERROR_MEMBER_NOT_IN_GROUP } } else { if ( # Check that the group exists (Get-LocalGroup -Name $Group -ErrorAction SilentlyContinue) -and # Check that the user exists in the group (Get-LocalGroupMember -Group $Group -Member $UserName -ErrorAction SilentlyContinue) ) { Write-Output "Found $UserName in Group $Group, removing." try { # Remove user from Group, -Confirm:$false used to not prompt and stop the script Remove-LocalGroupMember -Group $Group -Member $UserName -Confirm:$false Write-Output "Removed User $UserName from Group $Group" } catch { Write-Error -Message "Failed to remove User $UserName from Group $Group" exit 529 # ERROR_MEMBER_NOT_IN_GROUP } } elseif (-not (Get-LocalGroup -Name $Group -ErrorAction SilentlyContinue)) { Write-Error -Message "Group $Group does not exist" exit 528 # ERROR_NO_SUCH_GROUP } elseif (-not (Get-LocalGroupMember -Group $Group -Member $UserName -ErrorAction SilentlyContinue)) { Write-Error -Message "User does not exist in Group $Group" exit 529 # ERROR_MEMBER_NOT_IN_GROUP } } } elseif ($Action -like "Add") { if ($PSVersionTable.PSVersion.Major -lt 3) { # Connect to localhost try { $ADSI = [ADSI]("WinNT://$env:COMPUTERNAME") } catch { Write-Error -Message "Failed to connect to $env:COMPUTERNAME via ADSI object" exit 1 } # Find the group try { $ASDIGroup = $ADSI.Children.Find($Group, 'group') } catch { Write-Error -Message "Failed to find $Group via ADSI object" exit 1 } # Get the members of the group $GroupResults = try { $ASDIGroup.psbase.invoke('members') | ForEach-Object { $_.GetType().InvokeMember("Name", "GetProperty", $Null, $_, $Null) } } catch { $null } # Check if the user is in the group if ($UserName -in $GroupResults) { # User already in Group Write-Output "User $UserName already in Group $Group" exit 1320 # ERROR_MEMBER_IN_GROUP } else { # User not in group, add them to the group try { $ASDIGroup.Add(("WinNT://$env:COMPUTERNAME/$UserName")) } catch { Write-Error -Message "Failed to add User $UserName to Group $Group" exit 1388 # ERROR_INVALID_MEMBER } # We can verify the membership by running the following command: if ($UserName -in ( $ASDIGroup.psbase.invoke('members') | ForEach-Object { $_.GetType().InvokeMember("Name", "GetProperty", $Null, $_, $Null) } ) ) { # User in Group Write-Output "Added User $UserName to Group $Group" } else { Write-Error -Message "Failed to add User $UserName to Group $Group" exit 1388 # ERROR_INVALID_MEMBER } } } else { # Verify that the user and group exist if ( # Check that the user exists (Get-LocalUser -Name $UserName -ErrorAction SilentlyContinue) -and # Check that the group exists (Get-LocalGroup -Name $Group -ErrorAction SilentlyContinue) ) { # Check if user is already in group if (-not (Get-LocalGroupMember -Group $Group -Member $UserName -ErrorAction SilentlyContinue)) { # User not in group, good to add try { # Add user to group Add-LocalGroupMember -Group $Group -Member (Get-LocalUser -Name $UserName) Write-Output "Added User $UserName to Group $Group" } catch { Write-Error -Message "Failed to add User $UserName to Group $Group" exit 1388 # ERROR_INVALID_MEMBER } } else { # User already in Group Write-Output "User $UserName already in Group $Group" exit 1320 # ERROR_MEMBER_IN_GROUP } } } } } else { if ((Get-Module -Name ActiveDirectory -ListAvailable -ErrorAction SilentlyContinue)) { try { Import-Module -Name ActiveDirectory # Get most of our data needed for the logic, and to reduce the number of time we need to talk to AD $ADUser = (Get-ADUser -Identity $UserName -Properties SamAccountName -ErrorAction SilentlyContinue).SamAccountName $ADGroup = Get-ADGroup -Identity $Group -ErrorAction SilentlyContinue $ADInGroup = Get-ADGroupMember -Identity $Group -ErrorAction SilentlyContinue | Where-Object { $_.SamAccountName -like $ADUser } } catch { Write-Error -Message "Ninja Agent could not access AD, please check that the agent has permissions to add and remove users from groups." exit 5 # Access Denied exit code } # Modify AD User if ($Action -like "Remove") { # Verify that the user and group exist, and if the user is in the group if ( $ADUser -and # Check that the group exists $ADGroup -and # Check that the user exists in the group $ADInGroup ) { Write-Output "Found $UserName in Group $Group, removing." try { # Remove user from Group, -Confirm:$false used to not prompt and stop the script Remove-ADGroupMember -Identity $Group -Members $ADUser -Confirm:$false Write-Output "Removed User $UserName from Group $Group" } catch { Write-Error -Message "Failed to remove User $UserName from Group $Group" exit 529 # ERROR_MEMBER_NOT_IN_GROUP } } elseif (-not $ADGroup) { Write-Error -Message "Group $Group does not exist" exit 528 # ERROR_NO_SUCH_GROUP } elseif (-not $ADInGroup) { Write-Error -Message "User does not exist in Group $Group" exit 529 # ERROR_MEMBER_NOT_IN_GROUP } } elseif ($Action -like "Add") { # Verify that the user and group exist if ( # Check that the user exists $ADUser -and # Check that the group exists $ADGroup ) { # Check if user is already in group if (-not $ADInGroup) { # User not in group, good to add try { # Add user to group Add-ADGroupMember -Identity $Group -Members $ADUser Write-Output "Added User $UserName to Group $Group" } catch { Write-Error -Message "Failed to add User $UserName to Group $Group" exit 1388 # ERROR_INVALID_MEMBER } } else { # User already in Group Write-Output "User $UserName already in Group $Group" exit 1320 # ERROR_MEMBER_IN_GROUP } } } } else { # Throw error that RSAT: ActiveDirectory isn't installed Write-Error -Message "RSAT: ActiveDirectory is not installed or not found on this computer. The PowerShell Module called ActiveDirectory is needed to proceed." -RecommendedAction "https://docs.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2019-ps" exit 2 # File Not Found exit code } } } end {}
Access 300+ scripts in the NinjaOne Dojo
Detailed Breakdown
The script starts with a comprehensive commentary that offers insight into its functionalities, examples, and requirements. Essential parameters like Group, UserName, Action, and an optional IsDomainUser switch are defined, which control the main logic. A helper function, Test-IsElevated, checks if the script runs with administrative privileges. The core logic initiates with an elevation check, followed by whether the task concerns a local user or an Active Directory user. Depending on the PowerShell version and desired action (Add/Remove), the script interfaces with Active Directory Service Interfaces (ADSI) or employs native PowerShell cmdlets. For Active Directory users, the Active Directory module is employed, offering seamless integration and management.
Potential Use Cases
Case Study:
Sarah, an IT administrator in a growing firm, needs to onboard 50 new employees. With departments and roles varying, manual user allocation to respective AD groups would be time-consuming. Using this script, Sarah quickly assigns users to their respective groups, ensuring access controls are enforced efficiently. During quarterly IT audits, she also uses the script to remove users from specific groups or from the local machines of terminated employees.
Comparisons
Traditional user management usually revolves around GUI-based tools like Active Directory Users and Computers (ADUC) or Computer Management for local users. While they are user-friendly, they are not efficient for bulk operations. This script, leveraging PowerShell, ensures tasks that would take hours are reduced to mere minutes. However, unlike GUI tools that provide visual feedback, the script requires thorough testing to ensure no unintentional actions occur.
FAQs
- Can I use this script on older versions of PowerShell?
Yes, the script supports PowerShell versions as old as 2.0. However, functionality might vary based on the version. - Is the Active Directory module a mandatory requirement?
For actions concerning domain users, the Active Directory module is required. - How do I ensure I have the necessary administrative rights?
The script contains built-in checks for administrative rights and will provide an error message if not run with the required privileges.
Implications
The script can drastically reduce errors in user group management, which can have significant implications for IT security. Ensuring users are only part of the necessary groups enforces the principle of least privilege, a cornerstone of IT security. However, with automation comes the responsibility to ensure scripts don’t inadvertently provide excessive access, potentially opening doors to security breaches.
Recommendations
- Always test the script in a controlled environment before deploying in production.
- Maintain a log of all changes made using the script for auditing purposes.
- Ensure that you have backups of Active Directory or local user databases to revert any unintended changes.
Final Thoughts
As IT environments become more complex, tools like NinjaOne are instrumental in providing comprehensive solutions. For tasks like user group management, scripts such as the one detailed above can be integrated into platforms like NinjaOne, ensuring that IT administrators have the best tools at their fingertips, automating and streamlining processes while maintaining optimal security.