Suppress Exception in Office365 powershell - powershell

I have a script which adds members of an Active Directory group to an Office 365 distribution group. Many members of the AD group will also be members of the distribution group already, which causes the script to display the error:
Adding user#domain.com to distribution group GROUP
The recipient "user#domain.com" is already a member of the group "GROUP".
+ CategoryInfo : NotSpecified: (user#domain.com:RecipientWithAdUserGroupIdParameter`1)
[Add-DistributionGroupMember], MemberAlreadyExistsException
+ FullyQualifiedErrorId : [Server=HKNPR04MB0531,RequestId=84dc77fb-8cf4-4e2f-882e-0ce66b735d08,TimeStamp=9/02/2015 6:55:13 AM] [FailureCategory=Cmdlet-MemberAlreadyExistsException] 7CEFF683,Microsoft.Exchange.Management.RecipientTasks.AddDistributionGroupMember
+ PSComputerName : pod51055psh.outlook.com
I would like to suppress these errors, as I don't care if the members already exist.
I have tried catching the MemberAlreadyExistsException, setting -ErrorAction SilentlyContinue and catching all errors and writing "Error!" instead of the actual exception, however this does not seem to have taken effect.
Currently, my Try-Catch block looks like this:
try
{
Add-DistributionGroupMember -Identity $DistributionGroupName -Member $MemberEmail
}
Catch [System.Management.Automation.RemoteException]
{
if($_.FullyQualifiedErrorId -match 'AlreadyExists')
{
Write-Output "`t $emailaddress is already a member of $DistributionGroupName."
}
else
{
Write-Output "`t $_.Exception"
}
}
I believe that this should alert me when a user already exists, however I still receive an exception message.

Thanks to #mjolinor, I was able to catch the exception and provide a more user-friendly message. My corrected code is below:
try
{
Add-DistributionGroupMember -Identity $DistributionGroupName -Member $EmailAddress -ErrorAction Stop
}
Catch [System.Exception]
{
if($_.FullyQualifiedErrorId -match 'AlreadyExists')
{
Write-Output "`t $emailaddress is already a member of $DistributionGroupName."
}
else
{
Write-Output "`t $_.Exception"
}
}
I added the -ErrorAction Stop as suggested, which allowed me to Catch the exception. I also modified the exception type to catch all exceptions. Interestingly (in my opinion), the Catch block failed if I did not put an exception type in there.
Program output is now:
Adding user#domain.com to distribution group GROUP
user#domain.com is already a member of GROUP.

Related

PowerShell try-catch exit loop before second try occurs

I'm trying to loop a list of users where it is unknown if it contains English or Swedish and in addition to this check I need to know if there is an account called "CMG*" before executing the main task.
The problem is that my first 'Try' runs the lines as expected but the second one is ignored, so it apparently exit the loop on 'continue' it seems.
Can you not have 2 sets of 'Try-Catch' inside an 'if' statement?
What I have tried is to flip the two sets of 'Try-Catch' and it results in whatever is first in the loop execute and the bottom one is ignored.
foreach($User in $Users){
if($User."ADattribute"){
try {
if(!(Get-MailboxFolderPermission -Identity "$($User.UserPrincipalName):\Calendar" | Where-Object User -like 'CMG*')){
Add-MailboxFolderPermission -Identity "$($User.UserPrincipalName):\Calendar" -User <SecretUser> -AccessRights Reviewer
}
continue
}
catch [System.Management.Automation.RemoteException] {
Write-Host ":\Calendar could not be found"
}
try {
if(!(Get-MailboxFolderPermission -Identity "$($User.UserPrincipalName):\Kalender" | Where-Object User -like 'CMG*')){
Add-MailboxFolderPermission -Identity "$($User.UserPrincipalName):\Kalender" -User <SecretUser> -AccessRights Reviewer
}
continue
}
catch [System.Management.Automation.RemoteException] {
Write-Host ":\Kalender could not be found."
}
}
}
I think you are missing the function of the keyword continue. When executing the continue keyword, the current iteration stops and it continues with the next iteration. So I think if you remove the continue keyword(s) that your script will work as excepted.
https://devblogs.microsoft.com/scripting/powershell-looping-the-continue-statement/

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.

Catch error and restart the if statement

I have a powershell script that adds a computer to a domain. Sometimes, when I run the script I get the following error and when I run it for the second time it works.
How can I make the script to check if I get this error, and if so then to retry adding it to the domain?
I have read that it is hard to try and catch errors like that. Is that correct? Is there a better/different way to catch the error?
Thank you!
Code:
if ($localIpAddress -eq $newIP)
{ # Add the computer to the domain
write-host "Adding computer to my-domain.local.. "
Add-Computer -DomainName my-domain.local | out-null
} else {...}
Error:
This command cannot be executed on target computer('computer-name') due to following error: The specified domain either does not exist or could not be contacted.
You can use the built in $Error variable. Clear it before executing code, then test if the count is gt 0 for post error code.
$Error.Clear()
Add-Computer -DomainName my-domain.local | out-null
if($Error.count -gt 0){
Start-Sleep -seconds 5
Add-Computer -DomainName my-domain.local | out-null}
}
You could setup a function to call itself on the Catch. Something like:
function Add-ComputerToAD{
Param([String]$Domain="my-domain.local")
Try{
Add-Computer -DomainName $Domain | out-null
}
Catch{
Add-ComputerToAD
}
}
if ($localIpAddress -eq $newIP)
{ # Add the computer to the domain
write-host "Adding computer to my-domain.local.. "
Add-ComputerToAD
} else {...}
I haven't tried it to be honest, but I don't see why it wouldn't work. It is not specific to that error, so it'll infinitely loop on repeating errors (i.e. another computer with the same name exists in AD already, or you specify an invalid domain name).
Otherwise you could use a While loop. Something like
if ($localIpAddress -eq $newIP)
{ # Add the computer to the domain
write-host "Adding computer to my-domain.local.. "
While($Error[0].Exception -match "The specified domain either does not exist or could not be contacted"){
Add-Computer -DomainName my-domain.local | out-null
}
}