Sunday, October 2, 2022
HomePowershellMaking a Microsoft 365 Automated Off-boarding Course of with SharePoint, Graph API,...

Making a Microsoft 365 Automated Off-boarding Course of with SharePoint, Graph API, and PowerShell – The Lazy Administrator


On this write-up I shall be making a fundamental off-boarding automation that makes use of SharePoint because the entrance finish, and PowerShell, the Graph API, and Azure Runbooks because the back-end. HR will enter the customers UPN or Electronic mail, offboard date/time, and a forwarding tackle to ahead e mail to. As soon as the off-boarding datetime is inside 1hr the automation will examine the person in Azure AD to make sure its legitimate, the forwarding person is legitimate in Azure AD, doc in SharePoint the customers e-mail tackle, any and all licenses, and all group memberships. After that, it can proceed with the off-boarding the place it can take away all licenses from the person, take away all group memberships, and ahead e mail to our forwarding person. It’s going to log every thing again to SharePoint the place one can evaluation it.

Off-Boarding Phases

Pending

In Pending now we have simply submitted our person and the automation has not seen it, or it has self-cleared from any and all errors it had in earlier runs and is submitting the job again to automation. On subsequent run, or first run, it can examine to confirm the person is present in Azure AD and the forwarding person is present in Azure AD.

Acknowledged

On this stage, automation has seen the person and confirmed that there aren’t any points with it. If the off-boarding datetime is to be accomplished in 1 hours or much less it can proceed with the off-boarding. The next will occur:

  1. Licenses shall be eliminated
  2. Mailbox shall be auto forwarded to our forwarding person
  3. Group Memberships shall be eliminated

Full

On this stage, the person has been off-boarded with out points.

Error

On this stage, there was an error encountered someplace within the course of. Shall we say the person was inputted incorrectly and the automation couldn’t discover it in Azure AD. If we come again and see the error and alter the UPN, the following time the automation runs it can re-check that person and try to self-clear itself. If that occurs it can put itself again in Pending once more so it might proceed with the method as regular.

Logging

The automation will log each step of the method to a discipline in SharePoint. It’s going to log details about the person, steps within the automation and any errors it comes throughout.

Submitting a Consumer for Off-Boarding

In SharePoint, when submitting an new person to be off-boarded, HR is barely requested a number of particulars. The automation will do the remaining.

Create SharePoint Entrance-Finish

First, we have to create a SharePoint web site OR a brand new SharePoint listing in an present web site. In my instance, I’m going to create a brand new Staff Web site

In my instance, my web site is for Human Assets as they are going to be in command of the off-boarding course of so I created a SharePoint web site referred to as “Human Assets”

Subsequent, I’m going to create a brand new Listing inside the web site.

My listing shall be referred to as “Consumer Offboarding”

As soon as I’ve my new SharePoint Listing, I wish to add some columns which can be part of my off-boarding course of. By default I’ve the “Title” column which would be the major column. Subsequent, I’ll add the next columns:

  1. Electronic mail: Single string
  2. Teams: A number of strains
  3. Licenses: A number of strains
  4. Mailbox Sort: single string
  5. Forwarding Deal with: single string
  6. Standing: Alternative
  7. Notes: a number of strains

I may also change “Title” to Consumer for the customers Consumer Principal Title of UPN. Under is an outline on the opposite fields

Electronic mail: Doc the person’s e-mail tackle(s)

Teams: Doc the person’s Teams earlier than we clear them

Licenses: Doc the person’s licenses earlier than cleared

Mailbox Sort: Write again the Mailbox kind (shared or person mailbox) as we shall be changing the mailbox to shared

Forwarding Deal with: Write again the person that e mail is forwarding to.

Standing: The standing the automation could also be in.

Notes: Warning, errors, or success messages.

As soon as that’s all accomplished, my SharePoint listing will appear like the next screenshot:

Subsequent, lets modify the Standing to a ‘Alternative’ choice. I made it Pending (default), Acknowledged, Error and Full.

When a brand new person is entered it can get a default worth of Pending, which implies the automation has not seen this entry but. Acknowledged implies that automation has seen this person, Error means one thing was improper and Full implies that the person was offboarded with out points.

Subsequent, lets additionally add a date that the person needs to be offboarded. HR might pre-load customers into the offboarding instrument.

The OffboardDate worth will look pleasant in SharePoint however right here is the worth we are going to see within the back-end: 9/7/2022 7:00:00 AM

Get Column Info for Automation

Subsequent, we have to get some back-end info from our SharePoint listing utilizing SharePoint PnP PowerShell Module. Set up the PnP Module to proceed.

Set up-Module -Title PnP.PowerShell

Utilizing the PnP module hook up with your SharePoint web site that incorporates the listing. In my instance my web site is: https://bwya77.sharepoint.com/websites/HumanResources

Subsequent, use Get-PnPList cmdlet to get the ID to your newly created listing. Pay attention to the ID worth.

Get-PnPList -Identification "Consumer Offboarding"

Subsequent, we have to get the IDs of all of the columns in our listing utilizing Get-PnPField. NOTE: The sphere “Title” will at all times be “Title” despite the fact that we re-named it.

Get-PnPField -Listing "Consumer Offboarding"

Create Azure AD Utility

The following merchandise on the listing is to create an Azure AD Utility so we will interface with it and hook up with the Microsoft Graph API. Naviagte to the Azure AD Portal and to to Azure Lively Listing > App Registrations and choose New Registration. Give your new Utility a legitimate anme and a redirect URI. Pay attention to the Utility (consumer) ID and tenantID worth.

Subsequent, go to Certificates and Secret and click on “New Shopper Secret”

Be aware the key worth. You won’t be able to view it once more.

Utilizing the next operate, enter your clientID, tenantID and clientSecret and you must get again a bearer token.

Operate Join-GraphAPI {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$clientID,
        [Parameter(Mandatory)]
        [string]$tenantID,
        [Parameter(Mandatory)]
        [string]$clientSecret
    )
    start {
        $ReqTokenBody = @{
            Grant_Type    = "client_credentials"
            Scope		  = "https://graph.microsoft.com/.default"
            client_Id	  = $clientID
            Client_Secret = $clientSecret
        }
    }
    course of {

        $tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" -Technique POST -Physique $ReqTokenBody

    }
    finish {
        return $tokenResponse
    }

}

Get Web site ID

Subsequent, we have to use the Graph API to get our siteID the place our listing is. Within the App registration we have to grant the correct permissions.

Within the Utility in Azure Lively Listing go to API Permissions and grant the Websites.ReadWrite.All (this will look like overkill however we are going to want this permission later)

Subsequent, hook up with the Graph API and run the next code to show info in your websites. Pay attention to the id vaule

$token = Join-GraphAPI -clientID '2a53-9b61-4b45-aa2-2453007ff7' -tenantID '643c9-54e9-4ce-981-f00c21f' -clientSecret 'rbx8Q~SaPZUKelYjWmmw4JfcWU'
$apiUrl="https://graph.microsoft.com/v1.0/websites/"
$Knowledge = Invoke-RestMethod -Headers @{Authorization = "Bearer $($Token.access_token)"} -Uri $apiUrl -Technique Get
$Websites = ($Knowledge | select-object Worth).Worth

$Websites

View Listing Particulars and Knowledge

Subsequent, we should put some check knowledge in our SharePoint Listing. If you don’t do that, you’ll not see any fields within the subsequent step.

With the information in place, we will now proceed to view our fields and objects inside the listing utilizing PowerShell. By working the code beneath, we will view our newest entry.

Operate Get-ListItems {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$siteID,
        [Parameter(Mandatory)]
        [string]$listID,
        [Parameter(Mandatory)]
        [string]$accessToken
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/websites/$siteID/lists/$listID/objects?broaden=fields"
    }
    course of {
        $listItems = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique GET
    }
    finish {
        return $listItems.worth.fields
    }
}

Create our Automation Features

Now we have to create the code base that shall be doing the automation on our behalf. First lets create a operate to simply seek for our entered person.

Searchfor-Consumer

The next operate will take a UPN (which is my first discipline in my listing, despite the fact that its named as Consumer) and search Azure Lively Listing for stated person. Add the code to our script that incorporates the Get-ListItems operate and the Join-GraphAPI operate.

Additionally, you will must grant the next permission to your Utility so you’ll be able to run the API request: Listing.ReadWrite.All (once more, this will look like extra permission than is required however you have to this in a while, so we don’t must waste time granting simply Listing.Learn.All)

operate Searchfor-Consumer
{
	param (
		[system.string]$UPN,
		[system.string]$AccessToken
	)
	Start
	{
		$request = @{
			Technique = "Get"
			Uri    = "https://graph.microsoft.com/v1.0/customers/?`$filter=(userPrincipalName eq '$UPN')"
			ContentType = "utility/json"
			Headers = @{ Authorization = "Bearer $AccessToken" }
		}
	}
	Course of
	{
		$Knowledge = Invoke-RestMethod @request
	}
	Finish
	{
        return $Knowledge
	}
}

Set-ListItemField

The following operate we are going to create is the Set-ListItemField operate which can write knowledge again to objects inside the listing. Pay shut consideration, the Area param is simply taking during which discipline we wish to modify however inside the Physique payload is the place we set the cell. Every merchandise inside the physique should match Precisely to the column names that we obtained earlier. Be aware that the Consumer discipline goes to the Title, as a result of now we have to have that by default despite the fact that we renamed it. Additionally the Electronic mail column title is definitely E_x002d_Mail as a result of I had a ‘-‘.

operate Set-ListItemField
{
	Param (
		[Parameter(Mandatory)]
		[system.string]$AccessToken,
		[Parameter(Mandatory)]
		[System.String]$Area,
		[Parameter(Mandatory)]
		[System.Int32]$ItemNumber,
        [Parameter(Mandatory)]
        $Knowledge,
        [Parameter(Mandatory)]
        [System.String]$SiteID,
        [Parameter(Mandatory)]
        [System.String]$ListID
	)
	Start
	{
		If ($Area -eq "Consumer") {
			$Physique = @"
{
    "Title": "$Knowledge"
}
"@
		} ElseIf ($Area -eq "Electronic mail") {
			$Physique = @"
{
    "E_x002d_Mail": "$Knowledge"
}
"@
		} ElseIf ($Area -eq "Notes") {
			$Physique = @"
{
    "Notes": "$Knowledge"
}
"@
		} ElseIf ($Area -eq "Standing") {
			$Physique = @"
{
    "Standing": "$Knowledge"
}
"@
		} ElseIf ($Area -eq "Licenses") {
			$Physique = @"
{
    "Licenses": "$Knowledge"
}
"@
        } ElseIf ($Area -eq "MailboxType") {
        $Physique = @"
{
    "MailboxType": "$Knowledge"
}
"@
	} ElseIf ($Area -eq "ForwardingAddress") {
        $Physique = @"
{
    "ForwardingAddress": "$Knowledge"
}
"@
	} ElseIf ($Area -eq "MailboxFullAccess") {
        $Physique = @"
{
    "MbxFullAccess": "$Knowledge"
}
"@
	} ElseIf ($Area -eq "Teams") {
        $Physique = @"
{
    "Teams": "$Knowledge"
}
"@
    }
}
	Course of
	{
        $request = @{
			Technique = "Patch"
			Uri    = "https://graph.microsoft.com/v1.0/websites/$siteID/lists/$listID/objects/$itemnumber/fields"
			ContentType = "utility/json"
			Headers = @{ Authorization = "Bearer $($AccessToken)" }
			Physique   = $Physique
		}
		$Response = Invoke-RestMethod @request
	}
	Finish
	{
        return $Response
	}
}

 

Get-MailboxSettings

Subsequent, we have to create a operate to get the mailbox kind (common or shared) so we will write it again to the SharePoint listing. That is towards the beta endpoint at present so issues might change. Your utility will want the next permission: MailboxSettings.ReadWrite

 

operate Get-MailboxSettings {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$accessToken
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/beta/customers/$userPrincipalName/mailboxSettings"
    }
    course of {
        $mailboxSettings = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique GET
    }
    finish {
        return $mailboxSettings
    }
}

Get-MailboxForwarding

Subsequent, we have to examine mail guidelines to examine to see if now we have enabled forwarding e mail for a person. Sadly the MSGraph API doesn’t permit us to switch the Alternate settings in addition to we wish however we will nonetheless obtain mail forwarding via mail guidelines. I look to see if automation has set a forwarding rule by parsing the present guidelines and matching the show title.

operate Get-MailboxForwarding {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$accessToken,
        [Parameter()]
        [string]$RuleName="Automation - Offboarding Forwarding"
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/customers/$userPrincipalName/mailFolders/inbox/messageRules"
    }
    course of {
        $mailboxForwarding = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique GET

    }
    finish {
        return $mailboxForwarding.worth | The place-Object {$_.DisplayName -eq $RuleName}
    }
}

Set-MailboxForwarding

I have to additionally set the mailbox forwarding as effectively. The operate finds our person after which creates a high stage mail move rule inside the mailbox. Be aware: you probably have set exterior mail forwarding to be disabled inside your tenant (which you must!) this can nonetheless mean you can put in an inside customers e mail with out situation. In my code, I’m wanting up the person internally to make sure the settings are right after I create the rule.

operate Set-MailboxForwarding {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$accessToken,
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$ForwardingAddress,
        [Parameter()]
        [string]$ForwardingName,
        [Parameter()]
        [string]$RuleName="Automation - Offboarding Forwarding"
    )
    start {
        $headers = @{
            Authorization = "Bearer $($Token.access_token)"
        }

        $apiUrl = "https://graph.microsoft.com/v1.0/customers/[email protected]/mailFolders/inbox/messageRules"
    }
    course of {
        #Seek for our person in Azure AD. When you dont care to have your person be an inside person, you'll be able to skip this half and take away it
        $FwdUser = Searchfor-Consumer -UPN $ForwardingAddress -AccessToken $token.access_token
        #if we discovered our fwding person
        if ($FwdUser) {
            $ForwardingName = $FwdUser.displayName
            $ForwardingAddress = $FwdUser.mail

            $params = @{
                DisplayName = $RuleName
                Sequence = 1
                IsEnabled = $true
                Actions = @{
                    ForwardTo = @(
                        @{
                            EmailAddress = @{
                                Title = $ForwardingName
                                Deal with = $ForwardingAddress
                            }
                        }
                    )
                    StopProcessingRules = $true
                }
            }
            $physique = $params | ConvertTo-Json -Depth 10
    
            $mailboxForwarding = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique POST -Physique $physique -ContentType "utility/json" 
        }
    }
    finish {
        return $mailboxForwarding
    }
}

Take away-UserLicenses

The following merchandise up, is to take away a customers licenses. It will take a single LicenseSkuID so we will select to take away all or simply among the licenses from a person.

operate Take away-UserLicenses {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$accessToken,
        [Parameter(Mandatory)]
        [string]$LicenseSkuID
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/customers/$userPrincipalName/assignLicense"
    }
    course of {
        $physique = @{
            addLicenses = @()
            removeLicenses= @($LicenseSkuID)
        } | ConvertTo-Json -Depth 10
        $removeLicense = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique POST -Physique $physique -ContentType "utility/json"
    }
    finish {
        return $removeLicense
    }
}

Take away-GroupMembership

Take away-GroupMembership will take away our person from any and all Azure AD Teams they’re a member of.

operate Take away-GroupMembership {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userID,
        [Parameter(Mandatory)]
        [string]$accessToken,
        [Parameter(Mandatory)]
        [string]$GroupID
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/teams/$GroupID/members/$userID/`$ref"
    }
    course of {
        $removeGroupMember = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique DELETE
    }
    finish {
        return $removeGroupMember
    }
}

Get-GroupMembership

Lastly, we wish to get all of the teams our person is a member of and write it again to the SharePoint listing.

operate Get-GroupMembership {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$accessToken
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/customers/$userPrincipalName/memberOf"
    }
    course of {
        $groupMembers = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique GET
    }
    finish {
        return $groupMembers.worth | where-object {$_.roleTemplateId -eq $null}
    }
}

Automated Logic

Subsequent, we have to create the automated logic behind the automation. Every thing shall be based mostly on the standing the person entry is in.

Pending: Automation has not seen the person (or has resolved errors it noticed)

Acknowledged: Automation has seen the person and confirmed there aren’t any points with it (the person is in Azure AD, the forwarding person is in Azure AD)

Full: The person has been off boarded with none points.

Error: The person was not present in Azure AD or the forwarding person was not present in Azure AD.

Pending Standing

In Pending standing, the automation has both by no means seen this entry earlier than or the entry has self-healed from errors. On this state we have to lookup the person in Azure AD to confirm that every thing is right and lookup the forwarding person in Azure AD. If each of those go, we will proceed.

Under is a part of the script block for this logic. The complete automation runbook shall be accessible later

 if ($i.standing -eq "Pending")
    {
        #Solely search of the person if now we have not accomplished it prior 
        if ($i.notes -notlike "*Consumer was present in Azure AD*")
        {
            if ($Consumer) {
                $Notes += "Consumer was present in Azure AD`n"

                #Set the e-mail discipline within the SharePoint Listing
                $Notes += "Electronic mail Deal with: $($Consumer.mail)`n"
                Set-ListItemField -AccessToken $token.access_token -Area "Electronic mail" -ItemNumber $i.id -Knowledge $Consumer.Mail -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 

                #Get all licenses for the person
                $Licenses = Get-UserLicenses -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
                #Iterate via all licenses and create a clear array
                $Licenses | foreach-object {
                    $licenseArray += "$($_.skupartnumber) `n"
                }
                #Get the mailbox kind for the person (the property shall be userPurpose)
                $mailboxSettings = Get-MailboxSettings -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
                 
                #Write the mailbox kind for the person
                $Notes += "Mailbox Sort: $($mailboxSettings.userPurpose)`n"
                Set-ListItemField -AccessToken $token.access_token -Area "MailboxType" -ItemNumber $i.id -Knowledge $mailboxSettings.userPurpose -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2'   
                
                #Write the licenses the person has again to the SharePoint Listing
                $Notes += "Licenses: $($licenseArray)`n"
                Set-ListItemField -AccessToken $token.access_token -Area "Licenses" -ItemNumber $i.id -Knowledge $licenseArray -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 

                #Get the teams the person is a member of
                $groupMembership = Get-GroupMembership -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
                $grouparray = @()
                $groupMembership | foreach-object {
                    $Notes += "Including the Group: $($_.displayName)`n"
                    $grouparray += "$($_.displayName) `n"
                }
                Set-ListItemField -AccessToken $token.access_token -Area "Teams" -ItemNumber $i.id -Knowledge $grouparray -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
            }
            Else {
                $Notes += "Consumer was not present in Azure AD`n"
                #Set the standing to Error
                Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Error" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
            }
        }
        #Solely search of the forwarding person if now we have not accomplished it prior
        if ($i.notes -notlike "*Forwarding person was present in Azure AD*") {
            #See if the forwarding person is in Azure Lively Listing
            $ForwardingUser = Searchfor-Consumer -UPN $i.ForwardingAddress -AccessToken $token.access_token
            if ($ForwardingUser) {
                $Notes += "Forwarding person was present in Azure AD`n"
            }
            Else {
                $Notes += "Forwarding person was not present in Azure AD`n"
                #Set the standing to Error
                Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Error" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
            }
        }
        #If there have been no errors, then change the standing to Acknowledged
        $Notes += "Setting standing to Acknowledged`n"
        Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Acknowledged" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
    }

Acknowledged

If the checks within the Pending stage accomplished, we will proceed to the following logic. In Acknowledged, now we have confirmed the person info is right and if the offboarding time is inside 1hr we will proceed with the offboarding. On this stage we’re going to take away all person licenses, set mailbox forwarding, and take away the person from all teams.

 ElseIf ($i.standing -eq "Acknowledged")
    {
        #Determine what number of days and hours  till the person is to be off-boarded, if days left is lower than or equal to 0 and hours is lower than or equal to 0, then the person is to be off-boarded. NOTE: the default time within the timepicker is 7PM however may be modified in SharePoint
        $Timespan = New-TimeSpan -Begin (Get-Date) -Finish $i.OffboardDate
        if (($Timespan.days -le 0) -and ($timespan.hours -le 0))
        {
            #Take away liceses from the person
            #Get all licenses for the person
            $Licenses = Get-UserLicenses -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
            foreach ($license in $Licenses) {
                $Notes += "Eradicating $($license.skuPartNumber) license from $($i.Title)`n"
                Take away-UserLicenses -userPrincipalName $person.userPrincipalName -accessToken $token.access_token -licenseSkuID $license.skuId
            }

            #set the automated mail forwarding rule
            $Notes += "Setting computerized mail forwarding rule to ahead e mail to $($i.ForwardingAddress)`n"
            Set-MailboxForwarding -userPrincipalName $i.Title -accessToken $token.access_token -ForwardingAddress $i.ForwardingAddress
            $MailRuleCheck = Get-MailboxForwarding -userPrincipalName $i.Title -accessToken $token.access_token
            if ($MailRuleCheck) {
                $Notes += "Mail forwarding rule was set`n"
            }
            else {
                $Notes += "Mail forwarding rule was not set`n"
            }

            #Take away the person from the teams
            $teams = Get-GroupMembership -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
            foreach ($group in $teams) {
                $Notes += "Eradicating $($person.DisplayName) from $($group.displayName)`n"
                Take away-GroupMembership -userID $person.id -accessToken $token.access_token -groupID $group.id
            }

        #If there have been no errors, then change the standing to Full
        $Notes += "Setting standing to Full`n"
        Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Full" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 

        }
    }

Full

If the standing is moved into full then the person was off boarded. (no code for this as we don’t must do something).

Error

If the standing is in an error state, that implies that the person was not present in Azure AD (perhaps we fat-fingered the UPN) OR the forwarding person was not discovered. When in an error state, the automation will try to self-clear these every run. If we noticed it was in an error state and edited the entry to be right, on the following run it can examine, verify the person info is now right, delete the error message from the logs after which change the standing to Pending.

NOTE: All errors should be cleared to be moved to Pending state. If solely one in all two errors is resolved it can nonetheless keep in an error state.

Elseif ($i.standing -eq "Error")
    {
        #If we couldn't discover the person in Azure AD, try to self clear
        if ($i.notes -like "*Consumer was not discovered*") {
            $Consumer = Searchfor-Consumer -UPN $i.Title -AccessToken $token.access_token
            if ($Consumer) {
                $Notes = $Notes.Substitute("Consumer was not present in Azure AD","")
            }
        }
        #See if the error was due to the forwarding person not being in Azure AD
        if ($i.notes -like "*Forwarding person was not discovered*") {
            $ForwardingUser = Searchfor-Consumer -UPN $i.ForwardingAddress -AccessToken $token.access_token
            if ($ForwardingUser) {
                $Notes = $Notes.Substitute("Forwarding person was not present in Azure AD","")
            }
        }

        If ($Notes -notlike "*not*")
        {
            #If our notes include no errors, we all know all have cleared and we will set the standing to Pending once more
            Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Pending" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
        }
    }

Arrange the Azure Runbook

Create your Useful resource Group

First, I’ll create my useful resource group to accommodate my runbook and automation account. The one necessary half right here is to position every thing in the identical timezone as your self, so when it does the datetime math we aren’t coping with conversions.

Create Automation Account

Subsequent, we have to create the automation account for the runbook.

Modify the Runbook

Subsequent ,we have to retailer our secrets and techniques securely within the Automation Account. Go to Variables and add the clientID, clientSecret, and tenantID that we obtained earlier for our Azure AD utility. Be sure that you choose that they’re encrypted. We’ll retrieve the values within the runbook by utilizing the code beneath:

$clientId = Get-AutomationVariable -Title "clientID"
$tenantID = Get-AutomationVariable -Title "tenantID"
$clientSecret = Get-AutomationVariable -Title "clientSecret"

#Hook up with MSGraph API
$token = Join-GraphAPI -clientID $clientId -tenantID $tenantID -clientSecret $clientSecret

Create the Runbook

Subsequent, we have to create the precise runbook. Right here I chosen 5.1 however PSCore will do as effectively.

Then populate the runbook with the code beneath. Please notice, you have to to vary the listID and siteID to match your individual, in addition to the sphere names if they don’t match. In any other case it’s all re-usable.

Operate Join-GraphAPI {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$clientID,
        [Parameter(Mandatory)]
        [string]$tenantID,
        [Parameter(Mandatory)]
        [string]$clientSecret
    )
    start {
        $ReqTokenBody = @{
            Grant_Type    = "client_credentials"
            Scope		  = "https://graph.microsoft.com/.default"
            client_Id	  = $clientID
            Client_Secret = $clientSecret
        }
    }
    course of {

        $tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" -Technique POST -Physique $ReqTokenBody

    }
    finish {
        return $tokenResponse
    }

}
Operate Get-ListItems {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$siteID,
        [Parameter(Mandatory)]
        [string]$listID,
        [Parameter(Mandatory)]
        [string]$accessToken
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/websites/$siteID/lists/$listID/objects?broaden=fields"
    }
    course of {
        $listItems = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique GET
    }
    finish {
        return $listItems.worth.fields
    }
}
operate Searchfor-Consumer
{
	param (
		[system.string]$UPN,
		[system.string]$AccessToken
	)
	Start
	{
		$request = @{
			Technique = "Get"
			Uri    = "https://graph.microsoft.com/v1.0/customers/?`$filter=(userPrincipalName eq '$UPN')"
			ContentType = "utility/json"
			Headers = @{ Authorization = "Bearer $AccessToken" }
		}
	}
	Course of
	{
		$Knowledge = Invoke-RestMethod @request
	}
	Finish
	{
        return $Knowledge.worth
	}
}
operate Set-ListItemField
{
	Param (
		[Parameter(Mandatory)]
		[system.string]$AccessToken,
		[Parameter(Mandatory)]
		[System.String]$Area,
		[Parameter(Mandatory)]
		[System.Int32]$ItemNumber,
        [Parameter(Mandatory)]
        $Knowledge,
        [Parameter(Mandatory)]
        [System.String]$SiteID,
        [Parameter(Mandatory)]
        [System.String]$ListID
	)
	Start
	{
		If ($Area -eq "Consumer") {
			$Physique = @"
{
    "Title": "$Knowledge"
}
"@
		} ElseIf ($Area -eq "Electronic mail") {
			$Physique = @"
{
    "E_x002d_Mail": "$Knowledge"
}
"@
		} ElseIf ($Area -eq "Notes") {
			$Physique = @"
{
    "Notes": "$Knowledge"
}
"@
		} ElseIf ($Area -eq "Standing") {
			$Physique = @"
{
    "Standing": "$Knowledge"
}
"@
		} ElseIf ($Area -eq "Licenses") {
			$Physique = @"
{
    "Licenses": "$Knowledge"
}
"@
        } ElseIf ($Area -eq "MailboxType") {
        $Physique = @"
{
    "MailboxType": "$Knowledge"
}
"@
	} ElseIf ($Area -eq "ForwardingAddress") {
        $Physique = @"
{
    "ForwardingAddress": "$Knowledge"
}
"@
	} ElseIf ($Area -eq "MailboxFullAccess") {
        $Physique = @"
{
    "MbxFullAccess": "$Knowledge"
}
"@
	} ElseIf ($Area -eq "Teams") {
        $Physique = @"
{
    "Teams": "$Knowledge"
}
"@
    }
}
	Course of
	{
        $request = @{
			Technique = "Patch"
			Uri    = "https://graph.microsoft.com/v1.0/websites/$siteID/lists/$listID/objects/$itemnumber/fields"
			ContentType = "utility/json"
			Headers = @{ Authorization = "Bearer $($AccessToken)" }
			Physique   = $Physique
		}
		$Response = Invoke-RestMethod @request
	}
	Finish
	{
        return $Response
	}
}
operate Get-UserLicenses {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$accessToken
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/customers/$userPrincipalName/licenseDetails"
    }
    course of {
        $userLicenses = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique GET
    }
    finish {
        return $userLicenses.worth
    }
}
operate Get-MailboxSettings {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$accessToken
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/beta/customers/$userPrincipalName/mailboxSettings"
    }
    course of {
        $mailboxSettings = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique GET
    }
    finish {
        return $mailboxSettings
    }
}
operate Set-MailboxForwarding {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$accessToken,
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$ForwardingAddress,
        [Parameter()]
        [string]$ForwardingName,
        [Parameter()]
        [string]$RuleName="Automation - Offboarding Forwarding"
    )
    start {
        $headers = @{
            Authorization = "Bearer $($Token.access_token)"
        }

        $apiUrl = "https://graph.microsoft.com/v1.0/customers/[email protected]/mailFolders/inbox/messageRules"
    }
    course of {
        #Seek for our person in Azure AD. When you dont care to have your person be an inside person, you'll be able to skip this half and take away it
        $FwdUser = Searchfor-Consumer -UPN $ForwardingAddress -AccessToken $token.access_token
        #if we discovered our fwding person
        if ($FwdUser) {
            $ForwardingName = $FwdUser.displayName
            $ForwardingAddress = $FwdUser.mail

            $params = @{
                DisplayName = $RuleName
                Sequence = 1
                IsEnabled = $true
                Actions = @{
                    ForwardTo = @(
                        @{
                            EmailAddress = @{
                                Title = $ForwardingName
                                Deal with = $ForwardingAddress
                            }
                        }
                    )
                    StopProcessingRules = $true
                }
            }
            $physique = $params | ConvertTo-Json -Depth 10
    
            $mailboxForwarding = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique POST -Physique $physique -ContentType "utility/json" 
        }
    }
    finish {
        return $mailboxForwarding
    }
}
operate Get-MailboxForwarding {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$accessToken,
        [Parameter()]
        [string]$RuleName="Automation - Offboarding Forwarding"
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/customers/$userPrincipalName/mailFolders/inbox/messageRules"
    }
    course of {
        $mailboxForwarding = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique GET

    }
    finish {
        return $mailboxForwarding.worth | The place-Object {$_.DisplayName -eq $RuleName}
    }
}
operate Take away-UserLicenses {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$accessToken,
        [Parameter(Mandatory)]
        [string]$LicenseSkuID
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/customers/$userPrincipalName/assignLicense"
    }
    course of {
        $physique = @{
            addLicenses = @()
            removeLicenses= @($LicenseSkuID)
        } | ConvertTo-Json -Depth 10
        $removeLicense = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique POST -Physique $physique -ContentType "utility/json"
    }
    finish {
        return $removeLicense
    }
}
operate Get-GroupMembership {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userPrincipalName,
        [Parameter(Mandatory)]
        [string]$accessToken
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/customers/$userPrincipalName/memberOf"
    }
    course of {
        $groupMembers = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique GET
    }
    finish {
        return $groupMembers.worth | where-object {$_.roleTemplateId -eq $null}
    }
}
operate Take away-GroupMembership {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$userID,
        [Parameter(Mandatory)]
        [string]$accessToken,
        [Parameter(Mandatory)]
        [string]$GroupID
    )
    start {
        $headers = @{
            Authorization = "Bearer $accessToken"
        }
        $apiUrl = "https://graph.microsoft.com/v1.0/teams/$GroupID/members/$userID/`$ref"
    }
    course of {
        $removeGroupMember = Invoke-RestMethod -Uri $apiURL -Headers $headers -Technique DELETE
    }
    finish {
        return $removeGroupMember
    }
}

$clientId = Get-AutomationVariable -Title "clientID"
$tenantID = Get-AutomationVariable -Title "tenantID"
$clientSecret = Get-AutomationVariable -Title "clientSecret"

#Hook up with MSGraph API
$token = Join-GraphAPI -clientID $clientId -tenantID $tenantID -clientSecret $clientSecret

#Get all objects inside the SharePoint Listing
$objects = Get-ListItems -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -accessToken $token.access_token -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
#Iterate via all customers
foreach ($i in $objects)
{
    #Get any and all notes which can be already within the discipline so we don't overwrite something 
    [array]$Notes = $i.Notes
    $licenseArray = @()
    #Seek for our person
    $Consumer = Searchfor-Consumer -UPN $i.Title -AccessToken $token.access_token


    if ($i.standing -eq "Pending")
    {
        #Solely search of the person if now we have not accomplished it prior 
        if ($i.notes -notlike "*Consumer was present in Azure AD*")
        {
            if ($Consumer) {
                $Notes += "Consumer was present in Azure AD`n"

                #Set the e-mail discipline within the SharePoint Listing
                $Notes += "Electronic mail Deal with: $($Consumer.mail)`n"
                Set-ListItemField -AccessToken $token.access_token -Area "Electronic mail" -ItemNumber $i.id -Knowledge $Consumer.Mail -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 

                #Get all licenses for the person
                $Licenses = Get-UserLicenses -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
                #Iterate via all licenses and create a clear array
                $Licenses | foreach-object {
                    $licenseArray += "$($_.skupartnumber) `n"
                }
                #Get the mailbox kind for the person (the property shall be userPurpose)
                $mailboxSettings = Get-MailboxSettings -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
                 
                #Write the mailbox kind for the person
                $Notes += "Mailbox Sort: $($mailboxSettings.userPurpose)`n"
                Set-ListItemField -AccessToken $token.access_token -Area "MailboxType" -ItemNumber $i.id -Knowledge $mailboxSettings.userPurpose -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2'   
                
                #Write the licenses the person has again to the SharePoint Listing
                $Notes += "Licenses: $($licenseArray)`n"
                Set-ListItemField -AccessToken $token.access_token -Area "Licenses" -ItemNumber $i.id -Knowledge $licenseArray -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 

                #Get the teams the person is a member of
                $groupMembership = Get-GroupMembership -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
                $grouparray = @()
                $groupMembership | foreach-object {
                    $Notes += "Including the Group: $($_.displayName)`n"
                    $grouparray += "$($_.displayName) `n"
                }
                Set-ListItemField -AccessToken $token.access_token -Area "Teams" -ItemNumber $i.id -Knowledge $grouparray -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
            }
            Else {
                $Notes += "Consumer was not present in Azure AD`n"
                #Set the standing to Error
                Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Error" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
            }
        }
        #Solely search of the forwarding person if now we have not accomplished it prior
        if ($i.notes -notlike "*Forwarding person was present in Azure AD*") {
            #See if the forwarding person is in Azure Lively Listing
            $ForwardingUser = Searchfor-Consumer -UPN $i.ForwardingAddress -AccessToken $token.access_token
            if ($ForwardingUser) {
                $Notes += "Forwarding person was present in Azure AD`n"
            }
            Else {
                $Notes += "Forwarding person was not present in Azure AD`n"
                #Set the standing to Error
                Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Error" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
            }
        }
        #If there have been no errors, then change the standing to Acknowledged
        $Notes += "Setting standing to Acknowledged`n"
        Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Acknowledged" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
    }
    ElseIf ($i.standing -eq "Acknowledged")
    {
        #Determine what number of days and hours  till the person is to be off-boarded, if days left is lower than or equal to 0 and hours is lower than or equal to 0, then the person is to be off-boarded. NOTE: the default time within the timepicker is 7PM however may be modified in SharePoint
        $Timespan = New-TimeSpan -Begin (Get-Date) -Finish $i.OffboardDate
        if (($Timespan.days -le 0) -and ($timespan.hours -le 0))
        {
            #Take away liceses from the person
            #Get all licenses for the person
            $Licenses = Get-UserLicenses -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
            foreach ($license in $Licenses) {
                $Notes += "Eradicating $($license.skuPartNumber) license from $($i.Title)`n"
                Take away-UserLicenses -userPrincipalName $person.userPrincipalName -accessToken $token.access_token -licenseSkuID $license.skuId
            }

            #set the automated mail forwarding rule
            $Notes += "Setting computerized mail forwarding rule to ahead e mail to $($i.ForwardingAddress)`n"
            Set-MailboxForwarding -userPrincipalName $i.Title -accessToken $token.access_token -ForwardingAddress $i.ForwardingAddress
            $MailRuleCheck = Get-MailboxForwarding -userPrincipalName $i.Title -accessToken $token.access_token
            if ($MailRuleCheck) {
                $Notes += "Mail forwarding rule was set`n"
            }
            else {
                $Notes += "Mail forwarding rule was not set`n"
            }

            #Take away the person from the teams
            $teams = Get-GroupMembership -userPrincipalName $person.userPrincipalName -accessToken $token.access_token
            foreach ($group in $teams) {
                $Notes += "Eradicating $($person.DisplayName) from $($group.displayName)`n"
                Take away-GroupMembership -userID $person.id -accessToken $token.access_token -groupID $group.id
            }

        #If there have been no errors, then change the standing to Full
        $Notes += "Setting standing to Full`n"
        Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Full" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 

        }
    }
    Elseif ($i.standing -eq "Error")
    {
        #If we couldn't discover the person in Azure AD, try to self clear
        if ($i.notes -like "*Consumer was not discovered*") {
            $Consumer = Searchfor-Consumer -UPN $i.Title -AccessToken $token.access_token
            if ($Consumer) {
                $Notes = $Notes.Substitute("Consumer was not present in Azure AD","")
            }
        }
        #See if the error was due to the forwarding person not being in Azure AD
        if ($i.notes -like "*Forwarding person was not discovered*") {
            $ForwardingUser = Searchfor-Consumer -UPN $i.ForwardingAddress -AccessToken $token.access_token
            if ($ForwardingUser) {
                $Notes = $Notes.Substitute("Forwarding person was not present in Azure AD","")
            }
        }

        If ($Notes -notlike "*not*")
        {
            #If our notes include no errors, we all know all have cleared and we will set the standing to Pending once more
            Set-ListItemField -AccessToken $token.access_token -Area "Standing" -ItemNumber $i.id -Knowledge "Pending" -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 
        }
    }

    #On the finish: write all notes to the listing
    Set-ListItemField -AccessToken $token.access_token -Area "Notes" -ItemNumber $i.id -Knowledge $Notes -listID '1baffb5c-d51a-4803-b534-2a83c3c867fd' -siteID 'bwya77.sharepoint.com,218d5607-899f-4ec4-888a-0657c4fa2b11,af51a2a9-880d-4109-a7b1-84962fafb8a2' 

}

Set a Recurrence

Lastly, I would like it to run routinely each 1 hour. So I’ll create a schedule for the runbook to run each 1 hours.

Placing all of it collectively

After 1 hour has handed. I can see in Azure that it ran with out situation.

After which within the Entrance-Finish I can see that it ran and there have been no points with my person. Subsequent time it runs by person shall be off-boarded (until in fact I modify the offbaording date!)

You’ll be able to construct upon this and even do issues similar to e mail IT when a brand new person was submitted, e mail the customers supervisor when they’re offboarded, ship a Groups chat, and so forth! I firstly needed to point out methods to set it up in a fundamental configuration first. Now I can inform my HR to make use of the discussion board to proceed with any off-boardings. I locked the SharePoint web site to only them so no person else can entry it.

Supply Code

I’ll try to vary the code right here however updated code may be discovered on GitHub

 

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments