Powershell Remove members from DLs says not there, but they are - powershell

I have a powershell script that is supposed to empty a distribution list. It gets the member list into an array. It then pipes the array into Remove-DistributionGroupMember. Each run will have some members throw 'X isn't a member of the group Y' errors. At the end of the loop, I re-enumerate the users and for everyone that 'isn't a member of the group' error got thrown, is still a member of the group. This is not consistent. Sometimes everything works, but usually there are different members of the group that fail.
$grp = (Get-DistributionGroup -Identity:$DLIST -ErrorAction:Stop)
$members = (Get-DistributionGroupMember -Identity:$grp -ErrorAction:Stop)
$members | % {
$member = $_
try {
Remove-DistributionGroupMember -Identity:$grp -Member:$_ -Confirm:$false -WarningAction:Stop -ErrorAction:Stop
}
catch {
$LogStream.WriteLine((Get-Date).toString() + " Error removing ${member}:`n" + $_.toString())
}
}
$members = (Get-DistributionGroupMember -Identity:$grp -ErrorAction:Stop)
write-host "Users in List:"
$members | % { $LogStream.WriteLine(" " + $_.ToString())}
3/20/2015 5:06:55 AM Error removing xxxxx.xxxx.com/North America/Spokane/Users/XXXXXX:
The recipient "xxxx.xxxx.com/North America/Spokane/Users/XXXXX" isn't a member of the group "CN=*YYYYY,OU=IT,DC=xxxxxxx,DC=xxxx,DC=com".
Users in List:
xxxxx.xxxx.com/North America/Spokane/Users/XXXXXX

Try adding this to the beginning of your script:
Set-ADServerSettings -ViewEntireForest $true
And I would also add the -ResultSize "Unlimited" to your Get- commands.

Related

Exception Handling with AD PowerShell

I am building a PowerShell script to create AD Groups (Global and DomainLocal) by Importing their names from a Csv file.
I am having a hard time handling exceptions that will be generated in case Groups already exist.
What I want to achieve is if the Groups do not exist by the name in Csv then PS should create them and show message "Groups have been created" and if they already exist then it should display "Groups already exist" line by line so that if one exists and the other one doesn't then it should display the corresponding message.
What is happening is that PS doesn't display a message when it has created groups and when exception does occur it displays message only for Global Group not Local.
Please advise
Here's the code -
Try {
New-ADGroup -Name TestGlobal -GroupCategory Security -GroupScope Global -ManagedBy TEMP01 -Description "Owner is TEMP01" -Path (Some OU)
} Catch [Microsoft.ActiveDirectory.Management.ADException] {
if ($_ -like "The specified group already exists") {
Write-Host “!!! GLOBAL GROUP ALREADY EXISTS !!!”
} elseif ($_ -eq $null) {
Write-Host " GLOBAL GROUP CREATED SUCCESSFULLY "
}
}
Try {
New-ADGroup -Name TestLocal -GroupCategory Security -GroupScope DomainLocal -ManagedBy TEMP02 -Description "Owner is TEMP02" -Path (Some OU)
} Catch [Microsoft.ActiveDirectory.Management.ADException] {
if ($_ -like "The specified group already exists") {
Write-Host “!!! LOCAL GROUP ALREADY EXISTS !!!”
} elseif ($_ -eq $null) {
Write-Host " LOCAL GROUP CREATED SUCCESSFULLY "
}
}
PowerShell is a bit strange in that, by default, errors are non-terminating. That means that errors will be output to the console, but it will just continue on to the next line of code as if nothing happened.
Unfortunately, Try blocks only respond to terminating errors.
You can change this behaviour. On both of your New-ADGroup lines, add this to the end:
-ErrorAction Stop
That will tell PowerShell that you want it to treat errors on that line as terminating.
If you want, you can do some more reading about it here:
https://blogs.technet.microsoft.com/heyscriptingguy/2014/07/09/handling-errors-the-powershell-way/
https://blogs.technet.microsoft.com/heyscriptingguy/2014/07/05/weekend-scripter-using-try-catch-finally-blocks-for-powershell-error-handling/

Throw message not being shown

I'm trying to print my own error messages using throw. Consider this example:
$computerName = $env:COMPUTERNAME
$adsi = [adsi]("WinNT://$computerName")
if (!($adsi.Children.Find($userGroup, 'group')))
{
throw "User group not found."
}
If the user group is incorrect, this error message is shown:
Exception calling "Find" with "2" argument(s): The group name could not be found.
Is there a way to show my throw message, rather than the generic exception?
try this:
$computerName = $env:COMPUTERNAME
$adsi = [adsi]("WinNT://$computerName")
try {
$adsi.Children.Find($userGroup, 'group')
}
catch{
throw "User group not found."
}
[adsi] has a habit of throwing terminating errors. This happens with Get-ADUser as well. This is why capturing the error in a try/catch (like in whatever's answer) is necessary.
As an alternative you can check if the group exists by querying all local groups first and see if yours exists.
$computerName = $env:COMPUTERNAME
$adsi = [adsi]("WinNT://$computerName")
$localGroups = $adsi.children | Where-Object{$_.SchemaClassName -eq "Group"}
If($userGroup -notin $localGroups.Name){
throw "Your group is in another castle."
}
or a variant
if(-not ($adsi.children | Where-Object{$_.SchemaClassName -eq "Group" -and $_.Name -eq $userGroup})){
throw "Your group is in another castle."
}
Depending on where you are continuing with this code it might be advantageous to store this information once.

Powershell try/catch/finally isn't executing right (or I've completely hosed it)

I have a script that checks for cyclic groups.
The script takes all groups in a domain (parent groups), checks the membership of those groups and adds any member with an objectClass of 'group' to an array (child groups).
The script then checks the child groups to see if the parent is a member of the child (yeah, it's allowed but still not a good idea).
I added a try/catch/finally block so I could get the actual group names instead of the truncated error message that PowerShell returns.
The problem is, the script stops at the first error it encounters instead of continuing on.
This is the first try/catch I've done, so please bear with me.
Here's the script:
$original_ErrorActionPreference = 'Continue'
$ErrorActionPreference = 'Stop'
Import-Module -Name ActiveDirectory
$domains = #('corp.com', 'dom1.corp.com', 'dom2.corp.com')
foreach($domain in $domains){
Write-Host $domain -ForegroundColor Yellow
$parents = Get-ADGroup -server $domain -Properties name,objectclass -Filter * #get all domain groups
write-host $parents.count
$table = #()
$pGroupCount = #($parents).Count
$record = #{
'Parent' = ''
'Child' = ''
'Nester' = ''
}
foreach($parent in $parents){
Write-Host $parent.name -ForegroundColor Green
The script works up to this point.
This is the part that fails-
try { #get members in the parent that are groups
$children = Get-ADGroupMember -Identity $parent | Where-Object{$_.ObjectClass -eq 'group'} | Select-Object name,distinguishedName,objectClass
} catch [Microsoft.ActiveDirectory.Management.Commands.GetADGroupMember]{
Write-Host $parent.name ' must be checked manually' -ForegroundColor blue -BackgroundColor Yellow
$parent.distinguishedName | Out-String -Width 4096 | Out-File -FilePath "$env:USERPROFILE\desktop\$domain-manualCheck.txt" -Width 5120 -Append
} finally {
$pGroupCount = $pGroupCount - 1
write-host $children.count ' - ' $children.name -ForegroundColor Gray
Write-Host $pGroupCount ' groups to go' -foregroundColor yellow
foreach($child in $children){ #get members in the children that are groups AND that have the same name as the parent
$nested = Get-ADGroupMember $child.name | Where-Object {$_.objectClass -eq 'group' -and $_.name -eq $parent.name}
$nestedCount = #($nested).count
if ($nestedCount -gt 0){
foreach($nester in $nested){
Write-Host $parent.name -ForegroundColor White
Write-Host $nestedCount -ForegroundColor Magenta
Write-Host $nester.name -ForegroundColor Cyan
$record.'Parent' = $parent.name
$record.'Child' = $child.name
$record.'Nester' = $nester.name
$objRecord = New-Object psobject -Property $record
$table += $objRecord
}
}
}
$table | Export-Csv -Path "$env:USERPROFILE\desktop\$domain-Group-Report.csv" -NoTypeInformation
$error | out-string -width 4096 | Out-File -FilePath "$env:USERPROFILE\desktop\$domain-Errors.txt" -Width 5120 -Append
}
}
}
$ErrorActionPreference = $original_ErrorActionPreference
As soon as the script hits the first group that has an issue, this is the error that's returned (#comments are added):
PS C:\Users\admin_j\Desktop> .\gtest.ps1
corp.com #current domain
283 #total group count
Exchange Servers #current group
6 - Exchange Install Domain Servers Exchange Install Domain Servers Exchange Install Domain Servers Exchange Install Domain Servers Exchange Install Domain Servers #6 groups within the parent, groups are from sub-domains
Exchange Install Domain Servers
282 groups to go
Get-ADGroupMember : Cannot find an object with identity: 'Exchange Install Domain Servers' under: 'DC=corp,DC=com'.
At C:\Users\admin_j\Desktop\gtest.ps1:46 char:15
+ $nested = Get-ADGroupMember $child.name | Where-Object $_.objectClass -eq ' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Exchange Install Domain Servers:ADGroup) [Get-ADGroupMember], ADIdentityNotFoundException
+ FullyQualifiedErrorId : Cannot find an object with identity: 'Exchange Install Domain Servers' under: 'DC=corp,DC=com'.,Microsoft.ActiveDirectory.Management.Commands.GetADGroupMember
Why, instead of writing the bad group (in this case 'Exchange Install Domain Servers' under: 'DC=corp,DC=com'.) to a file, did the script stop? The group does indeed exist.
Should I add another block to catch any 'object not found' errors and send those to a file?
Thank you!
As Will's comment implies, you have indeed hosed your catch clause by specifying a type literal not matching an exception you'd ever expect thrown.
The general syntax for a catch clause is as follows
catch [catch-type-list] <statement block>
Where [catch-type-list] is an optional list of exception types that the associated statement block will act as an exception handler for.
That means, that this catch clause:
catch [Microsoft.ActiveDirectory.Management.Commands.GetADGroupMem‌​ber] {
# ...
}
Will only ever handle errors caused by an exception of the type [Microsoft.ActiveDirectory.Management.Commands.GetADGroupMem‌​ber] - this is of course not an exception type, and so the associated statement block will never execute.
In order for your catch clause to make sense in this context, specify a relevant exception type:
try{
Get-ADGroupMember -Identity $parent
}
catch [Microsoft.ActiveDirectory.Management.ADServerDownException]{
# DC is unreachable, abort
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityResolutionException]{
# Group identity not resolved, add to list and continue
}
catch {
# Something else, completely unforeseen, happened, you might want to re-throw and return from your function
}
The last catch clause, in which the type list has been omitted is known as a general catch clause, and will handle any exception that didn't match any of the preceding catch clauses.

Powershell - Checking Security Group Members against another list

A bit of context, I am trying to get a list of users from a security group, then check if any of those users do not have an assigned XenDesktop.
Please forgive me, I only started using Powershell yesterday so my formatting is off. It first grabs the users from the AD group then checks if that user has an assigned desktop, which works but I what I can't seem to get working is that if the user is found in that list, I want it to move onto the next username, instead it continues to check against every machine and then onto the final bits of code.
$checkusernames = Get-ADGroupMember "***AD security Group***" | Select SamAccountName
$desktops = get-brokerdesktop -DesktopGroupName Personal_WIN8 | Select MachineName, #{Name='AssociatedUserNames';Expression={[string]::join(“;”, ($_.AssociatedUserNames))}}
foreach ($username in $checkusernames.SamAccountName) {
foreach ($desktop in $desktops) {
If ($desktop.AssociatedUserNames -like "*$username*") {
write-host $username "is assigned to" $desktop.machinename
}
write-host $username "is not assigned to a desktop"
}
Write-host $username "is not assigned to anything"
pause
}
If you want to exit from a ForEach loop early you can do so with the Break keyword. For example:
$checkusernames = Get-ADGroupMember "***AD security Group***" | Select SamAccountName
$desktops = get-brokerdesktop -DesktopGroupName Personal_WIN8 | Select MachineName, #{Name='AssociatedUserNames';Expression={[string]::join(“;”, ($_.AssociatedUserNames))}}
foreach ($username in $checkusernames.SamAccountName) {
$machine = ''
foreach ($desktop in $desktops) {
If ($desktop.AssociatedUserNames -like "*$username*") {
$machine = $desktop.machinename
break
}
}
If ($machine) {
write-host $username "is assigned to" $desktop.machinename
} Else {
write-host $username "is not assigned to a desktop"
pause
}
}
This will stop the current cycle of the inner ForEach loop (once it has completed in it's entirety) without interrupting the ongoing cycle of the outer one.
Per the dicussion in the comments, i've also reorganised the code so that you only get a single output dependent on whether a desktop is matched to a user or not and it only pauses if it does not find a match.

trying to catch error with Add-QADGroupMember

I'm writing a script to bulk add users from a CSV, then add groups from another user.
It's working fine, except that some groups i'm not able to add (I get access denied when using the AD MMC - we have to get a different group to add them for us). The problem is that Add-QADGroupMember silently fails. I would like to somehow catch the error and list the groups that I have to get added by the different group.
I've tried try/catch, but it doesn't work... i'm at a loss.
Here is the code at the moment:
$users = Import-Csv .\UserList.csv
foreach ($user in $users) {
$SameAs = $user.SameAs
$UserGroups = (Get-QADUser $SameAs).MemberOf
foreach ($group in $UserGroups) {
Add-QADGroupMember $group -Member $user.SamAccountName |Out-Null
}
}
I'm just not able to get it to throw an error or exception when it fails to add a group.
Thanks for any help.
You have to set the erroraction to "stop".
I had the same problem with PowerCLI and all commands from there.
Try it so:
Add-QADGroupMember $group -Member $user.SamAccountName -ErrorAction Stop |Out-Null
or you can set the ErrorActionPreference global with
$ErrorActionPreference = "Stop"