howto restart code from the top

Hi all,

I’ve this code and it’s working just fine the only thing I want to be able to is to restart from the top if a user is still active when this code is executed

[pre]

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

foreach ($user in $users) {
#check if the user is disabled

$accountEnabled = Get-AzureADUser -ObjectId $user.Email
if(!($accountEnabled.AccountEnabled)) {

#get the users objectID from Azure
$UserObjectID =get-AzureAdUser -objectId $user.Email |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
$a= $userObjectIDGroupMemberShip.count
$Groupsremoved = 0
$ErrorGroup = 0

foreach ($group in $UserObjectIDGroupMemberShip) {

#remove the user from each indivudual group
try {

Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $SelectUserObjectID
$Groupsremoved++

}
catch {
#the groups that cannot be removed are safed in the error log

“Error removing $group : $($_)” |Add-content $ErrorLog
#write-host “$errorcount groups could not be removed from $($user.samaccountname)”
$Errorgroup++

}
finally {
#output on screen

}
}
write-host “#####################################################################”
write-host “result for $($user.Email)”
write-host “Total groups Found: $a”
Write-host “Total groups Removed: $Groupsremoved”
Write-host “Total groups not removed: $Errorgroup”
write-host “#####################################################################”
}
Else {
write-host “$($user.Email) cannot be removed at this time”

}

}

[/pre]
if the account is still active I’m correctly diverted into the Else part, is there a way that I can re-launch this code for a second attempt?
or do I simply need to call the script again by adding ./mypowershell.ps1 ?

 

thanks for your input in this matter

You could turn the section of code you need to reuse into a function or simply store it in a variable as a scriptblock. if you’re passing parameters to it, I would say a function. I’m not sure which part you are needing to rerun, so this is just an example.

Function Remove-AzureGroupMember {
    Param($Users,$Errorlog)

    foreach ($user in $users) {
        #check if the user is disabled

        $accountEnabled = Get-AzureADUser -ObjectId $user.Email
        if(!($accountEnabled.AccountEnabled)) {

        #get the users objectID from Azure
        $UserObjectID =get-AzureAdUser -objectId $user.Email |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
        $a= $userObjectIDGroupMemberShip.count
        $Groupsremoved = 0
        $ErrorGroup = 0

        foreach ($group in $UserObjectIDGroupMemberShip) {

        #remove the user from each indivudual group
        try
        {
            Remove-AzureADGroupMember -ObjectId $group.objectID -MemberId $SelectUserObjectID
            $Groupsremoved++
        }
        catch
        {
            #the groups that cannot be removed are safed in the error log

            “Error removing $group : $($_)” |Add-content $ErrorLog
            #write-host “$errorcount groups could not be removed from $($user.samaccountname)”
            $Errorgroup++
        }
        finally {
        #output on screen

        }
        }
        write-host “#####################################################################”
        write-host “result for $($user.Email)”
        write-host “Total groups Found: $a”
        Write-host “Total groups Removed: $Groupsremoved”
        Write-host “Total groups not removed: $Errorgroup”
        write-host “#####################################################################”
        }
        Else {
        write-host “$($user.Email) cannot be removed at this time”

        }

    }
}

Now that it’s a function, you can choose how to handle calling it again and how many times. For example, we can predefine 3 attempts per user.

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

$users | foreach {
    foreach($attempt in 1..3)
    {
        Remove-AzureGroupMember -Users $_ -Errorlog “c:\temp\groupdeletionErrors.txt”
        Start-Sleep -Seconds 1
    }
}

You could also run the entire list through, keeping track of the successes and failures, and then run the failing users through again. Hopefully you find this helpful. Just for completeness, here is how you can assign to and call scriptblock.

$script = {
    Param($InputString)

    write-host $InputString
    
}

And you can call it like

& $script "Good luck, Paul!"

or

. $script "Good luck, Paul!!"

One difference between the two calling conventions is the . method will retain script scoped variables in the calling scope. See this example for clarification of what I mean.

$script = {
    Param($InputString)

    write-host $InputString
    
    $scriptscopedvariable = "internal variable"
}
& $script "Good luck, Paul!"
$scriptscopedvariable

Output

Good luck, Paul!

But using the .

$script = {
    Param($InputString)

    write-host $InputString
    
    $scriptscopedvariable = "internal variable"
}
. $script "Good luck, Paul!!"
$scriptscopedvariable

Output

Good luck, Paul!!
internal variable

Hi Doug

thanks for the explanation of the function Since I’m learning and a complete noob in functions I’ve got a question concerning this part

[pre]

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

$users | foreach {
    foreach($attempt in 1..3)
    {
        Remove-AzureGroupMember -Users $_ -Errorlog “c:\temp\groupdeletionErrors.txt”
        Start-Sleep -Seconds 1
    }
}
[/pre]

in my original code I already import this list
$users = import-csv c:\temp\csv\toRemove.csv

this means that you use this list twice?
for me it would just be sufficient to call the function again after lets say 2 min

I did find another alternative by modifying the Else statement by adding this code
[pre]

write-host “$($user.Email) cannot be removed at this time”
write-warning “Syncing between AD and AzureAD is slow, we have to wait till the next Cycle is done…Sorry for the inconvenience”
start-sleep 120

./Offboarding2.ps1
[/pre]
and in the offboarding2 I’m checking again if when the last sync is done and when the script went through that cycle to continue with removing all the groups this user was member off

you way is obviously faster

would this work

[pre]

foreach($attempt in 1…3){

Remove-AzureGroupMember -Errorlog “c:\temp\groupdeletionErrors.txt”

Start-Sleep -Seconds 1

}

[/pre]

thanks again for your much appreciated input

Paul

I have never been able to figure out the backtic magic either, until now. I used the html char code and formatted that as code, seems to work `

Carriage return and line feed `r`n