Set-ADGroup is the PowerShell cmdlet for modifying an existing Active Directory group object. It can update common group properties directly through named parameters such as -Description, -DisplayName, -GroupScope, -GroupCategory, -HomePage, and -ManagedBy. For attributes that do not have a dedicated parameter, Microsoft documents -Add, -Remove, -Replace, and -Clear as the supported way to write values by LDAP display name. A group can be targeted by distinguished name, GUID, SID, or sAMAccountName, and you can also pipe a group object into the cmdlet or use the -Instance pattern. (Microsoft Learn)
For working administrators, the important distinction is this: Set-ADGroup changes the group object’s attributes, not the group’s membership workflow. Shallow guides often blur together three different jobs: changing attributes on the group, adding or removing members, and delegating who is allowed to manage membership. Those are related, but they are not the same operation in Active Directory.
What to check before you change anything
Before touching a production group, read the current values you care about. Microsoft notes that Get-ADGroup returns only a default set of properties unless you explicitly request more with -Properties. When you need custom or less common attributes, ask for them directly, or use -Properties * to inspect what is currently set. The same model applies to Get-ADUser when you need to resolve a user who will become the value of managedBy. (Microsoft Learn)
Use a writable domain controller, not an Active Directory snapshot or a read-only domain controller. Microsoft also documents -WhatIf for dry runs and -PassThru if you want the modified object returned immediately after the change. Those two switches make this cmdlet far safer when you are changing many groups or running through a CSV.
A practical pre-change read looks like this:
Get-ADGroup -Identity "HR-App-Users" `
-Properties Description,DisplayName,ManagedBy,extensionAttribute1 |
Select-Object Name,SamAccountName,Description,DisplayName,ManagedBy,extensionAttribute1
That pattern matters because the most common operator mistake with Set-ADGroup is not the write itself. It is writing blind, without first checking whether the current value is empty, inherited from a process, or already used by another automation path. Microsoft’s property retrieval model is built around that read-before-write flow.
The core rule: use direct parameters when they exist, LDAP display names when they do not
This is the part most admins actually need at their side while they work. Microsoft’s model is straightforward:
- use direct cmdlet parameters for common group properties
- use
-Replacewhen you want to overwrite the current value of an attribute that lacks a dedicated parameter - use
-Clearwhen you want the attribute emptied - use
-Addand-Removefor attributes where adding or removing individual values makes sense - when you use
-Add,-Remove,-Replace, and-Cleartogether, Microsoft performs them in this order:Remove,Add,Replace,Clear(Microsoft Learn)
That ordering is not trivia. It determines the final value when a script combines multiple operations in one call. If you write a bulk update that both appends and clears, -Clear wins because it runs last. If you both remove and replace, the replace happens after the remove. In other words, do not treat those hashtables as unordered intent; Active Directory does not.
Common direct property changes
For attributes that already have a named parameter, keep the command simple.
Set-ADGroup -Identity "HR-App-Users" `
-Description "Access group for the HR application" `
-DisplayName "HR App Users" `
-PassThru
This is the cleanest pattern for routine metadata changes because the cmdlet exposes those properties directly. Microsoft explicitly documents direct support for Description, DisplayName, GroupScope, GroupCategory, HomePage, ManagedBy, and SamAccountName, so there is no advantage in forcing those through -Replace unless you are standardizing everything around a generic function.
If you prefer staging multiple edits in memory before committing them, use the instance pattern:
$group = Get-ADGroup -Identity "HR-App-Users" -Properties Description
$group.Description = "Access group for the HR application"
Set-ADGroup -Instance $group -PassThru
Microsoft documents -Instance as the way to update a copied group object retrieved with Get-ADGroup, and only the properties that changed are written back. That makes it useful when your script builds changes conditionally before one final commit.
Setting ManagedBy the right way
-ManagedBy accepts an AD principal, and Microsoft documents the accepted identity forms as distinguished name, GUID, SID, or sAMAccountName. In practice, resolving the user first with Get-ADUser makes scripts clearer and avoids ambiguity when names are reused.
$owner = Get-ADUser -Identity "jsmith"
Set-ADGroup -Identity "HR-App-Users" -ManagedBy $owner.DistinguishedName -PassThru
The bigger nuance is what ManagedBy does not do. In the ADUC interface, the checkbox Manager can update membership list is not just a visual twin of the managedBy attribute. Microsoft documents that checkbox as adding an ACE that grants Write Members permission to the selected account. That means Set-ADGroup -ManagedBy sets the owner reference, but it does not by itself delegate membership management rights. If you need both, you must set managedBy and also adjust permissions separately.
That distinction matters operationally. Many admins set ManagedBy, then wonder why the designated owner still cannot maintain the group’s members. The reason is architectural: ownership metadata and ACL delegation are separate parts of the directory object.
Updating custom attributes and extensionAttribute values
When an attribute is not exposed as a named Set-ADGroup parameter, Microsoft’s documented path is to modify it by LDAP display name through -Add, -Remove, -Replace, or -Clear. The LDAP display name is the name LDAP clients use to read and write the attribute.
For Exchange custom attributes, Microsoft documents that the schema attributes are labeled as ms-Exch-Extension-Attribute1 through ms-Exch-Extension-Attribute15, and the schema reference for the first attribute shows its LDAP display name as extensionAttribute1. So in an environment where that attribute exists on the target object, the correct Set-ADGroup pattern is to address it as extensionAttribute1, not by the schema cn.
A straightforward overwrite looks like this:
Set-ADGroup -Identity "HR-App-Users" `
-Replace @{extensionAttribute1 = "Tier2-Owned"} `
-PassThru
And clearing it looks like this:
Set-ADGroup -Identity "HR-App-Users" -Clear extensionAttribute1
For day-to-day administration, -Replace is usually the clearest pattern for a single-value custom attribute because it expresses overwrite intent directly. The deeper rule is to always use the LDAP display name that the directory actually understands. If Get-ADGroup -Properties * does not show the attribute on your object, or the schema does not expose it in your environment, stop and verify the schema before you script around it.
Changing group scope or type without breaking assumptions
Set-ADGroup exposes both -GroupScope and -GroupCategory, which means the cmdlet can change scope and switch between security and distribution where Active Directory allows it. But Microsoft also documents important conversion rules. For example, a global group can become universal only if it is not a member of another global group. A universal group can become global only if it does not contain users or global groups from another domain. A universal group can become domain local only if it is not a member of any universal group or a domain local group from another domain. And going directly between global and domain local is not supported; you must go through universal as separate steps.
Set-ADGroup -Identity "HR-App-Users" -GroupScope Universal
Set-ADGroup -Identity "HR-App-Users" -GroupCategory Security
The operational risk is not just whether the command runs. Microsoft warns that changing a group’s type can affect ACEs, because access control ignores groups that are not security groups. Microsoft also notes that converting some scopes changes how the SID participates in access tokens across domains. So scope and type changes belong in a controlled change window, not in a casual metadata cleanup script.
Bulk updates from CSV
The same model scales well into a CSV-driven workflow. Microsoft explicitly documents Import-Csv as the way to bring structured file data into PowerShell objects, and New-ADGroup documentation even calls out Import-Csv as a supported pattern for creating multiple group objects. The same approach works for updates when you pair the imported rows with Set-ADGroup.
Example CSV:
GroupSam,Description,ManagedBySam,ExtensionAttribute1
HR-App-Users,Access group for the HR application,jsmith,Tier2-Owned
Payroll-Reviewers,Payroll review access,mbrown,Finance-Controlled
Example update script:
Import-Csv .\groups.csv | ForEach-Object {
$params = @{
Identity = $_.GroupSam
}
if ($_.Description) {
$params.Description = $_.Description
}
if ($_.ManagedBySam) {
$owner = Get-ADUser -Identity $_.ManagedBySam
$params.ManagedBy = $owner.DistinguishedName
}
if ($_.ExtensionAttribute1) {
$params.Replace = @{ extensionAttribute1 = $_.ExtensionAttribute1 }
}
Set-ADGroup @params -WhatIf
}
Run that with -WhatIf first, inspect the output, then remove -WhatIf for the actual change. That is the safest pattern because it combines documented structured import, documented identity resolution through Get-ADUser, and documented Set-ADGroup update semantics in one reviewable loop.
If you are creating groups, use New-ADGroup instead
One related search intent around this topic is “create AD group from CSV.” That is adjacent, but it is a different job. Microsoft documents New-ADGroup for object creation and notes that properties without direct parameters can be set through -OtherAttributes. Microsoft also explicitly lists Import-Csv with New-ADGroup as a supported method for creating multiple group objects.
Import-Csv .\new-groups.csv | ForEach-Object {
New-ADGroup `
-Name $_.Name `
-SamAccountName $_.SamAccountName `
-GroupScope $_.GroupScope `
-GroupCategory $_.GroupCategory `
-Path $_.Path `
-Description $_.Description
}
That separation is worth keeping clear in documentation and scripts: New-ADGroup creates the object, Set-ADGroup modifies its attributes later. Mixing those responsibilities usually makes bulk jobs harder to reason about.
Validation after the write
Always verify the result with a read that explicitly asks for the changed properties.
Get-ADGroup -Identity "HR-App-Users" `
-Properties Description,DisplayName,ManagedBy,GroupScope,GroupCategory,extensionAttribute1 |
Select-Object Name,Description,DisplayName,ManagedBy,GroupScope,GroupCategory,extensionAttribute1
That is the right ending state for this kind of operation: read the object, write the specific change, then read back exactly what you intended to modify. Microsoft’s default-property behavior on Get-ADGroup is the reason this last step should be explicit rather than assumed.
The practical mental model is simple. Use direct Set-ADGroup parameters for first-class group properties. Use LDAP display names with -Replace, -Add, -Remove, or -Clear for everything else. Treat ManagedBy as ownership metadata, not automatic delegation. And treat scope or type changes as design changes, not cosmetic edits. If you hold those lines, Set-ADGroup becomes predictable instead of fragile.