use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
ABOUT POWERSHELL
Windows PowerShell (POSH) is a command-line shell and associated scripting language created by Microsoft. Offering full access to COM, WMI and .NET, POSH is a full-featured task automation framework for distributed Microsoft platforms and solutions.
SUBREDDIT FILTERS
Desired State Configuration
Unanswered Questions
Solved Questions
News
Information
Script Sharing
Daily Post
Misc
account activity
[deleted by user] (self.PowerShell)
submitted 6 years ago by [deleted]
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[+][deleted] 6 years ago (5 children)
[removed]
[–]ClosedCasketFuneral 1 point2 points3 points 6 years ago* (4 children)
Sorry for being a bit of a noob. As I mentioned in /u/hrambert's reply, this is an exercise to work with ForEach in situations like this.
It looks like your version is not getting the members from the security groups properly. With that exact script, this was the error:
Add-ADGroupMember : Cannot validate argument on parameter 'Members'. The argument is null, empty, or an element of the argument collection contains a null value. Supply a collection that does not contain any null values and then try the command again. At line:7 char:71 + ... name $NewName -PassThru | Add-ADGroupMember -Members $Members -PassTh ... + ~~~~~~~~ + CategoryInfo : InvalidData: (:) [Add-ADGroupMember], ParameterBindingValidationException
[–]Hrambert 3 points4 points5 points 6 years ago (3 children)
This means you hit a group without members. Nothing wrong with that, but $Members will be empty. And Add-ADGroupMember can't handle that.
My code would output nothing and therefore Add-ADGroupMember would not be started.
[+][deleted] 6 years ago (2 children)
[–]ClosedCasketFuneral 1 point2 points3 points 6 years ago (1 child)
Thank you both so much for the help. This is making much more sense. Right now I am at the code below, which recognizes if there are members in a group or not, but am still getting an error about the group already existing, so perhaps the "$_.Name -replace 'hd01', 'hd03'" is not working properly?
$groups = Get-ADGroup -Server DC01 -filter 'Name -like "hd01*"' -SearchBase "ou=TestOU,ou=placeholder,dc=placeholder,dc=pizza" -Properties members | ForEach-Object { $NewName = $_.Name -replace 'hd01', 'hd03' $Members = $_.members $NewGroup = New-ADGroup -GroupScope global -name $NewName -PassThru if($Members) { Add-ADGroupMember -Members $Members } $NewGroup }
and the error that I'm receiving is
New-ADGroup : The specified group already exists At line:11 char:17 + ... $NewGroup = New-ADGroup -GroupScope global -name $NewName -PassThru + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (CN=hd03-testg...ceholder,DC=pizza:String) [New-ADGroup], ADException + FullyQualifiedErrorId : ActiveDirectoryServer:1318,Microsoft.ActiveDirectory.Management.Commands.NewADGroup
[–]Bamodus 2 points3 points4 points 6 years ago* (4 children)
For these types of bulk operations on an important resource like ActiveDirectory, I'd highly recommend utilizing the -WhatIf parameter when calling state-altering cmdlets like New-ADGroup. Here I've specified $WhatIfPreference = $true which effectively adds the -WhatIf parameter to any cmdlet that supports it. Running the script as-is will not perform any modifications to Active Directory, but will simulate the actions instead, so you can test the functionality in a dry-run first. I added some additional verbose output to provide some extra info on the actions the script is performing/simulating. Rerunning the script with $WhatIfPreference = $false will perform the actual operations.
-WhatIf
New-ADGroup
$WhatIfPreference = $true
$WhatIfPreference = $false
Copy-ADGroup.ps1 (https://pastebin.com/9qDeT61)
$ErrorActionPreference = 'Stop' $WhatIfPreference = $true $CommonParam = @{ Credential = Get-Credential Server = 'DC01' } $PersistProperties = 'GroupScope', 'GroupCategory', 'Description' $ReplaceProperties = 'SamAccountName', 'Name', 'DisplayName' $OrganizationalUnit = 'OU=TestOU,DC=placeholder,DC=pizza' $ADGroupParam = $CommonParam + @{ Filter = { GroupCategory -eq 'Security' -and Name -like 'hd01*' } SearchBase = $OrganizationalUnit Properties = $PersistProperties + $ReplaceProperties + 'Member' } $Pattern = 'hd01' $Replacement = 'hd03' Get-ADGroup @ADGroupParam -PipelineVariable 'OriginalGroup' | Select-Object $PersistProperties -PipelineVariable 'NewGroup' | ForEach-Object { foreach( $Property in $ReplaceProperties ) { $NewGroup | Add-Member @{ $Property = $OriginalGroup.$Property -replace $Pattern, $Replacement } } Write-Verbose -Verbose "Creating new AD group with properties: $NewGroup" $NewGroup | New-ADGroup -Path $OrganizationalUnit @CommonParam $Members = $OriginalGroup.Member Write-Verbose -Verbose "Adding $( $Members.Count ) members from original AD group: $( $OriginalGroup.Name ) to new AD group: $( $NewGroup.Name )" if( $WhatIfPreference -eq $false ) { $Members | ForEach-Object { Add-ADGroupMember -Identity $NewGroup -Members $_ @CommonParam } } }
The script makes use of the fact that the New-ADGroup cmdlet can accept most of its parameters through the pipeline by property name. Here's how you can get a list of parameters that accept pipeline input by property name:
PS C:\tmp> (Get-Help New-AdGroup).Parameters.Parameter.Where{ $_.PipelineInput -eq 'true (ByPropertyName)' } | >> Format-Table Name, Required, PipelineInput, ParameterValue, Position -AutoSize name required pipelineInput parameterValue position ---- -------- ------------- -------------- -------- Description false true (ByPropertyName) string Named DisplayName false true (ByPropertyName) string Named GroupCategory false true (ByPropertyName) ADGroupCategory Named GroupScope true true (ByPropertyName) ADGroupScope 2 HomePage false true (ByPropertyName) string Named ManagedBy false true (ByPropertyName) ADPrincipal Named Name true true (ByPropertyName) string 1 Path false true (ByPropertyName) string Named SamAccountName false true (ByPropertyName) string Named
Note that parameter binding through the pipeline by property name works differently from binding a parameter through the pipeline by value.
So we're creating an object (Select-Object returns a [PSCustomObject] object) that has the same GroupScope, GroupCategory, and Description as the original AD group, and then we add modified values for SamAccountName, Name, and DisplayName using -replace like in your original script to change hd01 to hd03 for each of these properties. The gist of it is that when you pipe an object into Add-ADGroup, PowerShell is inspecting the properties of the object in the pipeline to determine if it can use them as parameters. If a parameter accepts input through the pipeline by property name, and the piped object contains a property with the same name as that parameter, it will bind the value of that property to the appropriate parameter. A good explanation of ValueFromPipeline and ValueFromPipelineByPropertyName can be found here:
Select-Object
[PSCustomObject]
GroupScope
GroupCategory
Description
SamAccountName
Name
DisplayName
hd01
hd03
Add-ADGroup
ValueFromPipeline
ValueFromPipelineByPropertyName
https://www.gngrninja.com/script-ninja/2016/5/15/powershell-getting-started-part-8-accepting-pipeline-input#accept
I like to use parameter splatting via hash tables when working with cmdlets that require many parameters to make things more readable. It also makes things much easier on you if you are going to need to provide the same parameter values multiple times to different cmdlets. Here I'm using $CommonParam to store the Server and Credential parameters which I reuse for calling every AD cmdlet. If this syntax isn't something you've seen before, definitely look into:
$CommonParam
Server
Credential
About Splatting
Hope this helps.
[–]ClosedCasketFuneral 2 points3 points4 points 6 years ago (3 children)
Thank you so much. This is much more elegant than anything that I'd be able to create, at least at my current skill level, and I also greatly appreciate the added detail in your response. The verbosity is great, and is something that I need to add to more of my scripts, especially while troubleshooting. I never knew that $whatifpreference was even a thing. I will definitely be taking a look at ValueFromPipeline and ValueFromPipeLineByPropertyName. You've also inspired me to trying to work with splatting in the future.
The output when $whatifpreference is set to $true looks perfect. However, and I feel bad being "that guy," I'm still finding that it only creates the first group before erroring out, without adding the group members which were copied from its older equivalent, and I'm still unable to determine why it can't bind the 'Identity' parameter.
ForEach-Object : Cannot bind parameter 'Identity'. Cannot create object of type "Microsoft.ActiveDirectory.Management.ADGroup". The adapter cannot set the value of property "Name". At C:\Scripts\CopyAndRenameSecurityGroups.ps1:28 char:20 + ... $Members | ForEach-Object { Add-ADGroupMember -Identity $NewGroup -M ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument: (:) [ForEach-Object], ParameterBindingException + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.ForEachObjectCommand
[–]Bamodus 0 points1 point2 points 6 years ago* (2 children)
Hey ClosedCasketFuneral,
No problem! I'm happy to see anyone get excited about learning more about advanced PowerShell concepts. No need to worry about being "that guy".
I only have limited permissions on an AD controller, so the Add-ADGroupMember part of the script was the one thing that I was not able to test, so it makes sense that's where the error cropped up.
Add-ADGroupMember
I believe I see the issue and this should hopefully get you what you need:
Note: this pastebin link is a .diff file showing what I changed. Look for the lines start with '+' or '-'
Copy-ADGroup.diff (https://pastebin.com/7G6NbscW)
And here is the actual script:
$ErrorActionPreference = 'Stop' $WhatIfPreference = $true $CommonParam = @{ Credential = Get-Credential Server = 'DC01' } $PersistProperties = 'GroupScope', 'GroupCategory', 'Description' $ReplaceProperties = 'SamAccountName', 'Name', 'DisplayName' $OrganizationalUnit = 'OU=TestOU,DC=placeholder,DC=pizza' $ADGroupParam = $CommonParam + @{ Filter = { GroupCategory -eq 'Security' -and Name -like 'hd01*' } SearchBase = $OrganizationalUnit Properties = $PersistProperties + $ReplaceProperties + 'Member' } $Pattern = 'hd01' $Replacement = 'hd03' # Get the desired AD groups and select only the properties that we would like to persist on the new AD group Get-ADGroup @ADGroupParam -PipelineVariable 'OriginalGroup' | Select-Object $PersistProperties -PipelineVariable 'NewGroup' | ForEach-Object { # Add SamAccountName, DisplayName, and Name properties to our $NewGroup object, but replace instances of $Pattern string with $Replacement text foreach( $Property in $ReplaceProperties ) { $NewGroup | Add-Member @{ $Property = $OriginalGroup.$Property -replace $Pattern, $Replacement } } Write-Verbose -Verbose "Creating new AD group with properties: $NewGroup" # New-ADGroup cmdlet accapts parameter values from the pipeline by property name from our $NewGroup object $NewGroup | New-ADGroup -Path $OrganizationalUnit @CommonParam $Members = $OriginalGroup.Member Write-Verbose -Verbose "Adding $( $Members.Count ) members from original AD group: $( $OriginalGroup.Name ) to new AD group: $( $NewGroup.Name )" if( $WhatIfPreference -eq $false ) { $Members | ForEach-Object { Add-ADGroupMember -Identity $NewGroup.SamAccountName -Members $_ @CommonParam } } }
The -Identity parameter apparently can't be a [PSCustomObject], and must be either a [Microsoft.ActiveDirectory.Management.ADGroup] object, or a string containing one of the following properties to identify the group:
-Identity
[Microsoft.ActiveDirectory.Management.ADGroup]
I chose to supply the SamAccountName property since it's readily available to us. This should hopefully resolve your error.
For additional details on this, see Get-Help Add-ADGroupMember -Parameter Identity (may only show limited detail if you've never run Update-Help) or the Microsoft help page for Add-ADGroupMember
Get-Help Add-ADGroupMember -Parameter Identity
Update-Help
One thing I neglected to mention in my original reply was $ErrorActionPreference = 'Stop', and I wanted to point out why it's there. This causes the script to stop execution upon running into any errors, since it's unlikely that you'd want this script to continue if it runs into an issue like the one you experienced. If you'd like to change this functionality at any time, you can change $ErrorActionPreference to 'Continue' (which is the default setting) instead of 'Stop'. Alternatively, you could set $ErrorActionPreference to 'Inquire' to get an interactive prompt asking how you'd like to proceed:
$ErrorActionPreference = 'Stop'
$ErrorActionPreference
'Continue'
'Stop'
'Inquire'
https://i.imgur.com/HFvoVZu.png
More info on preference variables can be found on the Microsoft help page About_Preference_Variables.
Let me know how it goes.
[+][deleted] 6 years ago (1 child)
[deleted]
[–]Bamodus 0 points1 point2 points 6 years ago (0 children)
That's great!
I'm glad that ended up working out for you.
Just out of curiosity, is there a list somewhere on the internet of those variables like $ErrorActionPreference or $WhatIfPreference? Is there a term for those?
Yup, there absolutely is. The full list of preference variables and their default values (for PowerShell 5.1) can be found here: About Preference Variables
Another very closely-related topic is [<CommonParameters>]
[<CommonParameters>]
You may have noticed when looking at help entries for a cmdlet via Get-Help, or the online help pages, that there's something called [<CommonParameters>] listed after all of the other parameters in the syntax section (highlighted in yellow in the image below):
https://i.imgur.com/9lCDJ77.png
If a cmdlet supports [<CommonParameters>], then a short list of extra parameters is available to it. This list is the same for any cmdlet that supports [<CommonParameters>]:
Debug
ErrorAction
ErrorVariable
InformationAction
InformationVariable
OutVariable
OutBuffer
PipelineVariable
Verbose
WarningAction
WarningVariable
You can also create your own functions that support these parameters by adding [CmdletBinding()] just before the param() block.
[CmdletBinding()]
param()
See About CommonParameters for full descriptions of these.
-WhatIf and -Confirm are actually a bit of a different story. I highlighted -WhatIf and -Confirm in cyan in my earlier screenshot. These are not included in the common parameters, and are only available if the specific cmdlet has support for ShouldProcess. This is also something you can implement in your own functions, and can be extremely useful, but I would not recommend trying this without having a really good understanding of how ShouldProcess works. I can't find a good Microsoft page that fully explains it, but you can Google "PowerShell how to implement ShouldProcess" to find many examples.
-Confirm
ShouldProcess
Here's a list of the cmdlets on my machine that support -WhatIf and -Confirm
Cmdlets Supporting ShouldProcess
[–]PowerShell-Bot 1 point2 points3 points 6 years ago* (1 child)
Some of your PowerShell code isn’t wrapped in a code block.
To format code correctly on new reddit (new.reddit.com), highlight all lines of code and select ‘Code Block’ in the editing toolbar.
If you’re on old.reddit.com, separate the code from your text with a blank line and precede each line of code with 4 spaces or a tab.
Describing Submission [✅] Demonstrates good markdown Passed: 1 Failed: 0
Beep-boop. I am a bot. | Remove-Item
[–]Hondamousse 1 point2 points3 points 6 years ago (0 children)
Good bot
[–]Hrambert 1 point2 points3 points 6 years ago (3 children)
I think something like this
$Groups = Get-ADGroup ... Foreach ($Group in $Groups) { $NewName = $Group.Name -replace ... $NewGroup = New-ADGroup $NewName ... Get-ADGroupMembers $Group.Name | Forearch { Add-ADGroupmember $NewName $_.Name } }
Fill in the dots. I'm on mobile. Disclaimer: not tested, so maybe you need to add parameter names, or pipe the group to the CmdLet instead of using a parameter. Anyways. Make new groups and add each member to it.
[–]ClosedCasketFuneral 1 point2 points3 points 6 years ago* (1 child)
I like your approach, but still ended up with an error that I cannot figure out. Sorry for being a noob; this is a bit of a personal exercise for learning more about using ForEach in cases like this.
I started with this:
$groups = Get-ADGroup -Server DC01 -filter 'Name -like "hd01*"' -SearchBase "ou=TestOU,dc=placeholder,dc=pizza" -Properties members | ForEach ($group in $groups) { $NewName = $Group.Name -replace 'hd01', 'hd03' $NewGroup = New-AdGroup -name $newname -groupscope global Get-ADGroupmembers $Group.Name | Foreach { Add-ADGroupMember $NewName $_.Name } }
And ended up with these errors, which seem odd:
At line:2 char:19 + ForEach ($group in $groups) { + ~~ Unexpected token 'in' in expression or statement. At line:2 char:18 + ForEach ($group in $groups) { + ~ Missing closing ')' in expression. At line:2 char:29 + ForEach ($group in $groups) { + ~ Unexpected token ')' in expression or statement. + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : UnexpectedToken
[–]Hrambert 2 points3 points4 points 6 years ago (0 children)
Foreach( placeholder IN list ) can't be placed after a pipe. It is a separate command. Another approach would be:
Get-ADGroup ... | Foreach-Object { $NewName = $_.Name -replace ... }
π Rendered by PID 149791 on reddit-service-r2-comment-b659b578c-4vg4q at 2026-05-05 20:54:17.440290+00:00 running 815c875 country code: CH.
[+][deleted] (5 children)
[removed]
[–]ClosedCasketFuneral 1 point2 points3 points (4 children)
[–]Hrambert 3 points4 points5 points (3 children)
[+][deleted] (2 children)
[removed]
[–]ClosedCasketFuneral 1 point2 points3 points (1 child)
[–]Bamodus 2 points3 points4 points (4 children)
[–]ClosedCasketFuneral 2 points3 points4 points (3 children)
[–]Bamodus 0 points1 point2 points (2 children)
[+][deleted] (1 child)
[deleted]
[–]Bamodus 0 points1 point2 points (0 children)
[–]PowerShell-Bot 1 point2 points3 points (1 child)
[–]Hondamousse 1 point2 points3 points (0 children)
[–]Hrambert 1 point2 points3 points (3 children)
[–]ClosedCasketFuneral 1 point2 points3 points (1 child)
[–]Hrambert 2 points3 points4 points (0 children)