Solved

try catch and for loop

Posted on 2016-08-11
14
37 Views
Last Modified: 2016-08-15
I have a forEach loop. In the loop I have a try catch statement.  In the catch statement I would like to leave the current iteration of the loop and move to the next object. How do I do that?

Import-Csv "c:\scripts\sample.csv" | ForEach-Object {

try{
some code here
}
Catch{
some code here that will move on to the next object in the for loop.
$success = "false"
}

$success = "true"
}

Open in new window

0
Comment
Question by:Roccat
  • 5
  • 4
  • 3
  • +2
14 Comments
 
LVL 51

Expert Comment

by:Bill Prew
ID: 41752451
Try adding a continue statement at the end of the catch block, that's normally the way to jump to the next iteration of a loop.

~bp
0
 
LVL 39

Expert Comment

by:footech
ID: 41752527
Unless I'm misunderstanding your question, the above post isn't correct.
Using continue in a ForEach-Object loop will exit out of the loop, not go to the next object.
1
 

Author Comment

by:Roccat
ID: 41752529
You are correct footech. I accepted the answer before testing. I had to use "return"
0
 
LVL 12

Expert Comment

by:Dustin Saunders
ID: 41752562
Also, not sure how much code you're putting in Try but in your sample you can add your $success call to the try.

Import-Csv "c:\scripts\sample.csv" | ForEach-Object {

try{
some code here
$success = "true"
}
Catch{
some code here that will move on to the next object in the for loop.
$success = "false"
}
}

Open in new window


Depending on what you need to do for each item when true, you can call extended code in functions from the try loop to keep it all contained like that.
1
 
LVL 12

Expert Comment

by:Dustin Saunders
ID: 41752570
ex 2:
function DoThing1($object)
{
    try{
        some code here
        $success = "true"
    }
    Catch{
        some code here that will move on to the next object in the for loop.
        $success = "false"
    }
}

function DoThing2($object)
{
    try{
        some code here
        $success = "true"
    }
    Catch{
        some code here that will move on to the next object in the for loop.
        $success = "false"
    }
}

$objects = Import-Csv "c:\scripts\sample.csv"

foreach ($object in $objects)
{
    DoThing1 $object
    DoThing2 $object
}

Open in new window

0
 
LVL 39

Assisted Solution

by:footech
footech earned 250 total points
ID: 41752592
Unless you want to skip some code in the catch block, there's no need to use return.
In other scenarios if you want to skip some code in a loop, if you were using the foreach statement instead of the ForEach-Object cmdlet, there's more options for using keywords like break and continue.
0
 

Author Comment

by:Roccat
ID: 41752598
When I added the return to the catch block it returns to the foreach-object loop. It skips the rest of the code after the catch block. This is what I was aiming for.  Does this sound like the behavior you would expect?
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 12

Expert Comment

by:Dustin Saunders
ID: 41752686
Yes, the return terminates the current loop object run through, not the overall loop behavior in that context.  But depending on what you're looking to do there may be more efficient ways to handle the code.
0
 
LVL 39

Expert Comment

by:footech
ID: 41752690
Yes.  It would have been more accurate for me to state, "Unless you want to skip some code in the catch block or anything else in the loop after the return keyword..."

There's a variety of ways to do the equivalent.   Some pseudo code...
Import-Csv "c:\scripts\sample.csv" | ForEach-Object {
    try{
        some code here that might generate a terminating error
        some code which will not run if the previous line encountered an error
    }
    Catch{
        some code to run only if there was an error
    }
}

Open in new window

0
 
LVL 68

Expert Comment

by:Qlemo
ID: 41752799
From a programmer's viewpoint you should always try to have anything in the try block, not use anything like return or such. This applies even more for pipelines like with foreach-object.
0
 

Author Comment

by:Roccat
ID: 41752834
I use the return because if the aduser cant be created there is no use in executing the rest of the code.
0
 

Author Comment

by:Roccat
ID: 41752928
This is the script I am working on. If you notice the $ADUser block I use the try catch statement with the return in the catch.  

Import-Module ActiveDirectory
$ErrorCount = 0
$TotalCount = 0
$ErrorArray = @{}
$HomefolderErrorArray = @{}
$HomeFolderError = 0

#Modify Log file
$d = Get-Date
$log = "C:\log\userCreate.log"

function WriteLog($message)
{
 $d = Get-Date
    $out = "`n" , $d , $message
    Add-Content -Path $log -Value $out
}


<#
This is where the error count and log is cleared. The count is increased from the default
256 to 10,000. You should be able to call the error from the log like this $error[0]
#>

$error.clear()
$MaximumErrorCount = 10000


<#
This is where the CSV is imported then each row gets passed through the foreach-object loop. 
#> 
Import-Csv "C:\Scripts\accounts\first16.csv" | ForEach-Object {

$TotalCount++

#These are some sample write host statement to help troubleshoot.
#    Write-Host $_.Login
#     Write-Host $_.LastName
#      Write-Host $_.FirstName
#       Write-Host $_.'Job Title'
#        Write-Host $_.EID



<# 
OU Switch Case statement checks the Location field in the csv imported. the "$_." characters before each column name variable are needed to get the data from the csv.
 It checks to see if the location is the name of the school and then assigns the OU to the $OU variable which is referenced in the $aduser below in the script.
  The description variable is assigned also and this is used in the ad user creation $aduser below in the scrip;t. 
#>



        switch ($_.Location)
	{
		As {
			$OU = "OU=Staff,OU=As,dc=homelab,dc=com"
			$Description = "Staff - AS"
		}
		Bn {
			$OU = "OU=Staff,OU=Bn,dc=homelab,dc=com"
			$Description = "Staff - BN"
		}
		
		default
		{
			$OU = "OU=Staff,dc=homelab,dc=com"
			$Description = "Staff - "
		}
	}

<#
This if statement checks to see if the Logon name is taken and if it is 
it will change the logon variable value to the value of the login2 column
#>

if (dsquery user -samid $_.Login)
		{
			$LogonName = $_.Login2
         # Write-Host $OU
		}
		else
        {
            $LogonName = $_.Login
          #  Write-Host $LogonName
       }


<#
This is a splatted hash table that takes all the variables and assigns them to the $ADUser array 
and sends that array to the new-aduser command. 
#> 

try{

$fullname = $_.FirstName + " " + $_.LastName
    $NamePlace = "hello"
    $ADUser = [ordered]@{ }
	$ADUser['Name'] = $_.FirstName + " " + $_.LastName
	$ADUser['SamAccountName'] = $LogonName
	$ADUser['GivenName'] = $_.FirstName
	$ADUser['Surname'] = $_.LastName
    $ADUser['Description'] = $Description
	$ADUser['DisplayName'] = $_.FirstName + " " + $_.LastName
	$ADUser['UserPrincipalName'] = $LogonName + "@homelab.com"
	$ADUser['AccountPassword'] = ConvertTo-SecureString -AsPlainText 'P@ssw0rd' -Force
	$ADUser['Title'] = $_."Job Title"
	$ADUser['EmailAddress'] = $LogonName + "@homelab.com"
	$ADUser['Office'] = $_.EID
    $ADUser['Path'] = $OU
	$ADUser['Enabled'] = $True
	$ADUser['HomeDirectory'] = "\\homelab\staff\$LogonName"
	$ADUser['HomeDrive'] = 'H:'
	New-ADUser @ADUser 
}
catch{
$ErrorCount++
$ErrorArray[$TotalCount] = $fullname
return
}
  
<#
This adds the user just created to the 
googleapps group and yard-staff ou groups.
#>

	Add-ADGroupMember "googleapps" –Members $LogonName
	Add-ADGroupMember "yard-staff" –Members $LogonName


<#
This creates the homefolder on the shae in the set-acl statement below.
#>
    try{
	New-Item -type directory -path "\\dc-pc\share\$LogonName"
	$Acl = Get-Acl "\\dc-pc\share\$LogonName"
	$Ar = New-Object system.security.accesscontrol.filesystemaccessrule ("$LogonName", "Modify", "ContainerInherit, ObjectInherit", "None", "Allow")
	$Acl.SetAccessRule($Ar)
	Set-Acl "\\dc-pc\share\$LogonName" $Acl
    }
    catch {
    $HomeFolderError++
    $HomeFolderErrorArray[$TotalCount] = $fullname
    }
  
}
Write-Host ($ErrorArray | out-string)
    $SuccessfulCount = ($TotalCount - $ErrorCount)
 WriteLog("Accounts Attempted:  " + $TotalCount , "  Failures:  "+ $ErrorCount , "      Successful:  " + $SuccessfulCount ,"      Home Folder Errors:  " + $HomeFolderError , "`r`n`r`n", "Account Creation Error names",($ErrorArray  | out-string) , "Home Folder Creation Error names",($HomeFolderErrorArray  | out-string))

Open in new window

0
 
LVL 12

Accepted Solution

by:
Dustin Saunders earned 250 total points
ID: 41753036
I'd probably organize it like so, so it's easy to edit and work on.  Putting each step into a separate function makes it easier to change the catch in the event there's a problem, or if you need to add other items later on too.  I personally have an easier time reading and modifying code when it's laid out like this, but just one opinion.

Import-Module ActiveDirectory
$errorCount = 0
$totalCount = 0

$d = Get-Date
$log = "C:\log\userCreate.log"

function WriteLog($message)
{
    $d = Get-Date
    $out = "`n" , $d , $message
    Add-Content -Path $log -Value $out
}

function ProcessLocation($location)
{
    switch ($location)
	{
		As {
			$OU = "OU=Staff,OU=As,dc=homelab,dc=com"
			$Description = "Staff - AS"
		}
		Bn {
			$OU = "OU=Staff,OU=Bn,dc=homelab,dc=com"
			$Description = "Staff - BN"
		}
		default
		{
			$OU = "OU=Staff,dc=homelab,dc=com"
			$Description = "Staff - "
		}
	}
}

function GetLogin($user)
{
    if (dsquery user -samid $user.Login)
    {
	    $ln = $user.Login2
    }
    else
    {
        $ln = $user.Login
    }
    return $ln
}

function CreateUser($user)
{
    try {
    $fullname = $_.FirstName + " " + $_.LastName
    $NamePlace = "hello"
    $ADUser = [ordered]@{ }
	$ADUser['Name'] = $_.FirstName + " " + $_.LastName
	$ADUser['SamAccountName'] = $logonName
	$ADUser['GivenName'] = $_.FirstName
	$ADUser['Surname'] = $_.LastName
    $ADUser['Description'] = $Description
	$ADUser['DisplayName'] = $_.FirstName + " " + $_.LastName
	$ADUser['UserPrincipalName'] = $addr
	$ADUser['AccountPassword'] = ConvertTo-SecureString -AsPlainText 'P@ssw0rd' -Force
	$ADUser['Title'] = $_."Job Title"
	$ADUser['EmailAddress'] = $addr
	$ADUser['Office'] = $_.EID
    $ADUser['Path'] = $OU
	$ADUser['Enabled'] = $True
	$ADUser['HomeDirectory'] = "\\homelab\staff\$LogonName"
	$ADUser['HomeDrive'] = 'H:'
	New-ADUser @ADUser
    WriteLog("Created user $logonName.")
    } catch {
    WriteLog("Failed to create user $logonName.")
    }
}

function AddToGroups($logonName)
{
    try {
    Add-ADGroupMember "googleapps" –Members $logonName
    Add-ADGroupMember "yard-staff" –Members $logonName
    WriteLog("Added $logonName to googleapps and yard-staff.")
    } catch {
    WriteLog("Failed adding $logonName to googleapps and yard-staff.")
    $errorCount++
    }
}

function CreateDirectory($logonName)
{
    try {
    New-Item -type directory -path "\\dc-pc\share\$logonName"
    $acl = Get-Acl "\\dc-pc\share\$logonName"
    $ar = New-Object system.security.accesscontrol.filesystemaccessrule ("$logonName", "Modify", "ContainerInherit, ObjectInherit", "None", "Allow")
    $acl.SetAccessRule($ar)
    Set-Acl "\\dc-pc\share\$logonName" $acl
    WriteLog("Created and set permissions for \\dc-pc\share\$logonName .")
    } catch {
    WriteLog("Failed to create and set permissions for \\dc-pc\share\$logonName .")
    $errorCount++
    }
}

$users = Import-CSV "C:\Scripts\accounts\first16.csv"

foreach ($user in $users)
{
    $totalCount++
    ProcessLocation $user.Location
    $logonName = GetLogin $user
    $addr = $logonName + "@homelab.com"
    CreateUser $user
    AddToGroups $logonName
    CreateDirectory $logonName
}

Open in new window

0
 

Author Comment

by:Roccat
ID: 41753200
That is nicely laid out.  For some reason the users permissions are not applying to the homefolder when I try this dustin. trying to figure out why not.
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

In this previous article (https://oddytee.wordpress.com/2016/05/05/provision-new-office-365-user-and-mailbox-from-exchange-hybrid-via-powershell/), we made basic license assignments to users in O365. When I say basic, the method is the simplest way …
I thought I'd write this up for anyone who has a request to create an anonymous whistle-blower-type submission form created using SharePoint 2010 (this would probably work the same for 2013). It's not 100% fool-proof but it's as close as you can get…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now