how to catch the exceptions

Hi ,

I’ve got this code which should remove the user from all the groups a user is member of in azureAD.
Some of the groups I can’t remove due to restrictions which result in this type of error messages
Remove-AzureADGroupMember : Error occurred while executing RemoveGroupMember
Code: Request_BadRequest
Message: Unable to update the specified properties for objects that have originated within an external service.
RequestId: fc9382f2-4441-417e-a6e1-bb3bd05ef9d1
DateTimeStamp: Wed, 15 Apr 2020 06:11:45 GMT
HttpStatusCode: BadRequest
HttpStatusDescription: Bad Request
HttpResponseStatus: Completed
At line:9 char:34

  • … oupMember = Remove-AzureADGroupMember -ObjectId $group.objectID -Memb …
  • CategoryInfo : NotSpecified: (:slight_smile: [Remove-AzureADGroupMember], ApiException
  • FullyQualifiedErrorId : Microsoft.Open.AzureAD16.Client.ApiException,Microsoft.Open.AzureAD16.PowerShell.RemoveGroupMember

My question is how do I catch these errors so that these are not shown on the screen. can this be put in an error log?
my code is
[pre]

#connect to azureAD
Connect-AzureAD
#read file with users (email address)
$users = import-csv c:\temp\toRemove.csv

foreach ($user in $users) {
#get the users objectID from Azure
$UserObjectID =get-AzureAdUser -objectId $user.SamAccountName |select objectID
#grab the clean objectID from the user
$SelectUserObjectID= $userObjectID.objectID
#find all the groups a user is member off
$UserObjectIDGroupMemberShip = get-AzureAdUserMembership -objectID $SelectUserObjectID

foreach ($group in $UserObjectIDGroupMemberShip) {
#remove the user from each indivudual group
$removeAzureAdGroupMember = Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $SelectUserObjectID
}
}
[/pre]

thanks Paul

Did you consider implementing some error handling with try catch?

Get-Help about_try_catch_finally

thanks for the tip

 

if someone is interested here’s the final working script
[pre]

#connect to azureAD
Connect-AzureAD
#read file with users (email address)
$users = import-csv c:\temp\toRemove.csv
$ErrorLog = “c:\temp\groupdeletionErrors.txt”

foreach ($user in $users) {
#get the users objectID from Azure
$UserObjectID =get-AzureAdUser -objectId $user.SamAccountName |select objectID
#grab the clean objectID from the user
$SelectUserObjectID= $userObjectID.objectID
#find all the groups a user is member off
$UserObjectIDGroupMemberShip = get-AzureAdUserMembership -objectID $SelectUserObjectID

foreach ($group in $UserObjectIDGroupMemberShip) {
#remove the user from each indivudual group
try {
$removeAzureAdGroupMember = Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $SelectUserObjectID
}
catch {
#the groups that cannot be removed are safed in the error log
“Error removing $group” + “:” + “$_” |Add-Content $ErrorLog
}
finally {
#output on screen
Write-host "user is removed from " + “-” + $group.DisplayName
}
}
}

[/pre]

Great. Thanks for sharing.

Just as an aside note: You declare the variable $removeAzureAdGroupMember but you never use it. :wink: And you should do yourself and all others a favor and indent your code to make it easier readable. You may read the Powershell Best Practice and Style Guide.

If I’m understanding try…catch…finally correctly, your script will say the user is removed whether they are or not.

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-7

Check out the Free Resources link on the left, in the ebooks you’ll see an entry for the big book of powershell error handling.

Or here is the direct link

https://leanpub.com/thebigbookofpowershellerrorhandling

[quote quote=218898]Great. Thanks for sharing.

Just as an aside note: You declare the variable $removeAzureAdGroupMember but you never use it. :wink: And you should do yourself and all others a favor and indent your code to make it easier readable. You may read the Powershell Best Practice and Style Guide.

[/quote]

don’t quite understand your remark on the $removeAzureAdGroupMember because when I check the user in question the groups that can be removed are removed. concerning the the readability of the code I copy pasted it from my original powershell window and the indents are there

Unfortunately you deleted the code I was reffering to. If I remember right you had a code line like this …

$removeAzureAdGroupMember = Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $SelectUserObjectID

So you declare a variable $removeAzureAdGroupMember but in the code you posted you never used it. That’s what I meant.

@olaf,

I don’t understand why it got removed but here’s the code again

[pre]

#connect to azureAD
Connect-AzureAD
#read file with users (email address)
$users = import-csv c:\temp\toRemove.csv
$ErrorLog = “c:\temp\groupdeletionErrors.txt”

foreach ($user in $users) {
#get the users objectID from Azure
$UserObjectID =get-AzureAdUser -objectId $user.SamAccountName |select objectID
#grab the clean objectID from the user
$SelectUserObjectID= $userObjectID.objectID
#find all the groups a user is member off
$UserObjectIDGroupMemberShip = get-AzureAdUserMembership -objectID $SelectUserObjectID

foreach ($group in $UserObjectIDGroupMemberShip) {
#remove the user from each indivudual group
try {
$removeAzureAdGroupMember = Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $SelectUserObjectID
$removeAzureAdGroupMember
}
catch {
#the groups that cannot be removed are safed in the error log
“Error removing $group” + “:” + “$_” |Add-Content $ErrorLog
}
finally {
#output on screen
Write-host "user is removed from " + “-” + $group.DisplayName
}
}
}

[/pre]

here’s the code again and I’ve added the variable again however what I don’t understand at this point is why it’s working even without adding the second $removeAzureAdGroupMember

[pre]

#connect to azureAD
Connect-AzureAD
#read file with users (email address)
$users = import-csv c:\temp\toRemove.csv
$ErrorLog = “c:\temp\groupdeletionErrors.txt”

foreach ($user in $users) {
#get the users objectID from Azure
$UserObjectID =get-AzureAdUser -objectId $user.SamAccountName |select objectID
#grab the clean objectID from the user
$SelectUserObjectID= $userObjectID.objectID
#find all the groups a user is member off
$UserObjectIDGroupMemberShip = get-AzureAdUserMembership -objectID $SelectUserObjectID

foreach ($group in $UserObjectIDGroupMemberShip) {
#remove the user from each indivudual group
try {
$removeAzureAdGroupMember = Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $SelectUserObjectID
$removeAzureAdGroupMember
}
catch {
#the groups that cannot be removed are safed in the error log
“Error removing $group” + “:” + “$_” |Add-Content $ErrorLog
}
finally {
#output on screen
Write-host "user is removed from " + “-” + $group.DisplayName
}
}
}

[/pre]

ps don’t know what is wrong with the indents but can’t get it working correctly mea culpa

I don’t have access to an Azure AD ad the moment to test but I think this should do the same:

Connect-AzureAD
$users = import-csv c:\temp\toRemove.csv
$ErrorLog = 'c:\temp\groupdeletionErrors.txt'

foreach ($user in $users) {
    $UserObjectID = get-AzureAdUser -objectId $user.SamAccountName | Select-Object -Property objectID
    $UserObjectIDGroupMemberShip = get-AzureAdUserMembership -objectID $($userObjectID.objectID)

    foreach ($group in $UserObjectIDGroupMemberShip) {
        try {
            Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $SelectUserObjectID
        }
        catch {
            "Error removing $group : $($_)" | Add-Content $ErrorLog
        }
        finally {
            "user is removed from - $($group.DisplayName)"
        }
    }
}

You should avoid to comment obvious things. If the command is Connect-AzureAD even an unexperienced Powersheller knows that this command connects to an Azure AD. :wink: Powershell is quite self-explanatory.

In your try block you assign the output of Remove-AzureADGroupMember to a variable. Does this cmdlet actually produce any output? I wouldn’t expect it to by default. Even if it does - you assign a variable and output this variable right away and do not use it any further. So it’s not needed at all - that’s what I meant before. :wink:

What editor do you use to write your scripts? (Just out of my curiosity)

I use the powershell ISE editor

… hmm … strange … when I use the the ISE and paste some code the indentation is retained.

what do you use as editor notepad++ or a different one?

 

ISE and VSCode. Notepad++ only when I’m on a server and want to have a quick look into a script - not for development. :wink:

in addition to the above script, and I can’t figure out how to add this I would like to add the following

total nr of groups a user is member of
total nr of groups the user is removed from
total nr of groups we where not able to remove the user from

thanks for any pointers in this direction

Logs have their place, but you’re taking all of the analysis functionality of Powershell if you are not using objects. If you do something like this:

Connect-AzureAD
$users = import-csv c:\temp\toRemove.csv

$results = foreach ($user in $users) {
    $UserObjectID = get-AzureAdUser -objectId $user.SamAccountName | Select-Object -Property objectID
    $UserObjectIDGroupMemberShip = get-AzureAdUserMembership -objectID $($userObjectID.objectID)
    
    if ($UserObjectID) {
        foreach ($group in $UserObjectIDGroupMemberShip) {
            try {
                Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $SelectUserObjectID
                [PSCustomObject]@{
                    User = $user.SamAccountName
                    Group = $group.Name
                    Status = 'Success'
                }
            }
            catch {
                [PSCustomObject]@{
                    User = $user.SamAccountName
                    Group = $group.Name
                    Status = 'Failed - {0}' -f $_
                }
            }
        }
    }
    else {
        [PSCustomObject]@{
            User = $user.SamAccountName
            Group = $NULL
            Status = 'Failed - User not found.'
        }       
    }
}

$results

After you have the results, you can do analysis paralysis with Where-Object and Group-Object

#Mock data
$results = @()
$results += [PSCustomObject]@{
    User = 'User123'
    Group = 'Group123'
    Status = 'Success'
}
$results += [PSCustomObject]@{
    User = 'User123'
    Group = 'Group124'
    Status = 'Failed - Permission Denied'
}
$results += [PSCustomObject]@{
    User = 'User125'
    Group = $null
    Status = 'Failed - User Not Found.'
}

#Count by status
$results | Group-Object -Property Status

#Investigate failures
$results | Where{$_.Status -ne 'Success'}

#All failures grouped by user
$results | Where{$_.Status -ne 'Success'} | Group-Object -Property User

Output:

Count Name                      Group
----- ----                      -----
    1 Failed - Permission Deni… {@{User=User123; Group=Group124; Status=Failed - Permission Denied}}
    1 Failed - User Not Found.  {@{User=User125; Group=; Status=Failed - User Not Found.}}
    1 Success                   {@{User=User123; Group=Group123; Status=Success}}

User   : User123
Group  : Group124
Status : Failed - Permission Denied


User   : User125
Group  :
Status : Failed - User Not Found.

    1 User123                   {@{User=User123; Group=Group124; Status=Failed - Permission Denied}}
    1 User125                   {@{User=User125; Group=; Status=Failed - User Not Found.}}

@Rob thanks for your help however when I run your version there is nothing that is removed I get these errors

[pre]

User Group Status


user1 Failed - Error occurred while executing RemoveGroupMember …
user1 Failed - Error occurred while executing RemoveGroupMember …
user1 Failed - Error occurred while executing RemoveGroupMember …
user1 Failed - Error occurred while executing RemoveGroupMember …
user1 Failed - Error occurred while executing RemoveGroupMember …
user1 Failed - Error occurred while executing RemoveGroupMember …
user1 Failed - Error occurred while executing RemoveGroupMember …
user1 Failed - Error occurred while executing RemoveGroupMember …
user1 Failed - Error occurred while executing RemoveGroupMember …
user1 Failed - Error occurred while executing RemoveGroupMember …
{@{User=User123; Group=Group123; Status=Success}}
{@{User=User123; Group=Group124; Status=Failed - Permission Denied}}
{@{User=User125; Group=; Status=Failed - User Not Found.}}
User123 Group124 Failed - Permission Denied
User125 Failed - User Not Found.
{@{User=User123; Group=Group124; Status=Failed - Permission Denied}}
{@{User=User125; Group=; Status=Failed - User Not Found.}}
[/pre]
when I run my version the groups do get removed correctly can it be that this has something to do with the PScustomobject?

 

 

It was a suggestion, not tested code. When you do a direct lookup on an Id, it either returns the object or fails, so it needs to be wrapped in a try\catch with the -ErrorAction set to stop. Tested the following:

$creds = Get-Credential -UserName 'rob.simmers@mycompany.com' -Message 'Enter your credentials'

Connect-AzureAD -Credential $creds

#Emulate CSV
$users = @()
$users += [pscustomobject]@{SamAccountName = 'rob.simmers@mycompany.com'}
$users += [pscustomobject]@{SamAccountName = 'dont.exist@mycompany.com'}
$users += [pscustomobject]@{SamAccountName = 'john.smith@mycompany.com'}

#$users = import-csv c:\temp\toRemove.csv

$results = foreach ($user in ($users | Select -ExpandProperty SamAccountName)) {
    try {
        $UserObjectID = get-AzureAdUser -objectId $user | Select-Object -Property objectID -ErrorAction Stop
        $UserObjectIDGroupMemberShip = get-AzureAdUserMembership -objectID $($userObjectID.objectID)
    
        foreach ($group in $UserObjectIDGroupMemberShip) {
            try {
                Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $UserObjectID -ErrorAction Stop
                [PSCustomObject]@{
                    User   = $user
                    Group  = $group.DisplayName
                    Status = 'Success'
                }
            }
            catch {
                [PSCustomObject]@{
                    User   = $user
                    Group  = $group.DisplayName
                    Status = 'Failed - Group Removal. {0}' -f $_
                }
            }
        }
    }
    catch {
        [PSCustomObject]@{
            User   = $user
            Group  = $NULL
            Status = 'Failed - User lookup. {0}' -f $_
        }

    }          
}

$results

Output:

User                       Group                                   Status                                                                                                                                                                
----                       -----                                   ------                                                                                                                                                                
rob.simmers@mycompany.com Everyone                                Success                                                                                                                                                               
rob.simmers@mycompany.com All Company                             Success                                                                                                                                                               
rob.simmers@mycompany.com Strategic Vision Team                   Success                                                                                                                                                               
rob.simmers@mycompany.com Information Technology                  Success                                                                                                                                                               
dont.exist@mycompany.com                                          Failed - User lookup. Error occurred while executing GetUser ...                                                                                                      
john.smith@mycompany.com  Everyone                                Success                                                                                                                                                               
john.smith@mycompany.com  All Company                             Success                                                                                                                                                               
john.smith@mycompany.com  Marketing & Sales                       Success                                                                                                                                                               
john.smith@mycompany.com  Information Technology                  Success