Solved

try catch and for loop

Posted on 2016-08-11
14
48 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 53

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 40

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
Best Practices: Disaster Recovery Testing

Besides backup, any IT division should have a disaster recovery plan. You will find a few tips below relating to the development of such a plan and to what issues one should pay special attention in the course of backup planning.

 
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 40

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
 
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 40

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 69

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

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Create and license users in Office 365 in bulk based on a CSV file. A step-by-step guide with PowerShell script examples.
Windows 10 came with  a lot of built in applications, Some organisations leave them there, some will control them using GPO's. This Article is useful for those who do not want to have any applications in their image (example:me).
Email security requires an ever evolving service that stays up to date with counter-evolving threats. The Email Laundry perform Research and Development to ensure their email security service evolves faster than cyber criminals. We apply our Threat…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

828 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