Deep Dive: Building an Employee Onboarding Portal with Azure, PowerApps, and SharePoint

Introduction

Employee onboarding is one of the most critical processes in any organization. A seamless onboarding experience sets the tone for new hires, improves retention, and accelerates productivity. In this Deep Dive, we'll build a complete enterprise-grade employee onboarding portal that combines the best of Microsoft's cloud ecosystem:

  • Azure Functions for serverless automation and business logic
  • PowerApps for intuitive mobile-ready user interfaces
  • SharePoint Online for document management and workflow orchestration
  • Microsoft Graph API for user provisioning and calendar integration
  • Power Automate for approval workflows and notifications

This isn't a simple tutorialβ€”it's a production-ready solution with architecture diagrams, complete source code, security best practices, and real-world enterprise patterns.

What You'll Build:

  • Automated onboarding workflow from offer letter to first day
  • Custom PowerApps portal for HR and new employees
  • Azure Functions for user provisioning and Azure AD integration
  • SharePoint document libraries with metadata and retention policies
  • Email notifications and calendar appointments via Microsoft Graph
  • Manager approval workflow with Power Automate
  • Onboarding dashboard with progress tracking
  • Mobile-responsive interface for remote onboarding

Business Value:

  • Reduce onboarding time from 2 weeks to 3 days
  • Eliminate manual provisioning errors
  • Improve new hire satisfaction scores by 40%
  • Track compliance with onboarding checklists
  • Enable remote/hybrid onboarding workflows

Time to Complete: 3-4 hours
Skill Level: Advanced

Solution Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          Employee Onboarding Portal - Complete Architecture                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                              β”‚
β”‚                           User Access Layer                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   HR Manager     β”‚    β”‚   New Employee   β”‚    β”‚     Manager       β”‚   β”‚
β”‚  β”‚   (Web/Mobile)   β”‚    β”‚   (Web/Mobile)   β”‚    β”‚   (Web/Mobile)    β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚           β”‚                       β”‚                         β”‚              β”‚
β”‚           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚                       β”‚                      β”‚                             β”‚
β”‚                       β–Ό                      β–Ό                             β”‚
β”‚           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚           β”‚        PowerApps Canvas Application        β”‚                   β”‚
β”‚           β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                   β”‚
β”‚           β”‚  β€’ Onboarding Request Form                 β”‚                   β”‚
β”‚           β”‚  β€’ Document Upload Interface               β”‚                   β”‚
β”‚           β”‚  β€’ Task Checklist & Progress Tracker       β”‚                   β”‚
β”‚           β”‚  β€’ Manager Approval Screen                 β”‚                   β”‚
β”‚           β”‚  β€’ HR Dashboard & Analytics                β”‚                   β”‚
β”‚           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                           β”‚                                                 β”‚
β”‚                           β”‚ Connectors (Delegated Auth)                    β”‚
β”‚                           β”‚                                                 β”‚
β”‚           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚           β”‚               β”‚                            β”‚                   β”‚
β”‚           β–Ό               β–Ό                            β–Ό                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚  β”‚  SharePoint  β”‚  β”‚    Power     β”‚         β”‚  Azure Functions β”‚         β”‚
β”‚  β”‚   Online     β”‚  β”‚   Automate   β”‚         β”‚   (Serverless)   β”‚         β”‚
β”‚  β”‚  (Storage)   β”‚  β”‚  (Workflow)  β”‚         β”‚   (Business      β”‚         β”‚
β”‚  β”‚              β”‚  β”‚              β”‚         β”‚    Logic)        β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚         β”‚                 β”‚                          β”‚                    β”‚
β”‚         β”‚                 β”‚                          β”‚                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚  β”‚              Data & Integration Layer                        β”‚         β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€         β”‚
β”‚  β”‚                                                               β”‚         β”‚
β”‚  β”‚  SharePoint Lists & Libraries:                               β”‚         β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚         β”‚
β”‚  β”‚  β”‚ β€’ OnboardingRequests (Main workflow tracking)        β”‚   β”‚         β”‚
β”‚  β”‚  β”‚   - Employee info, start date, department, status    β”‚   β”‚         β”‚
β”‚  β”‚  β”‚   - Manager approval, HR approval, IT approval       β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ β€’ Documents (Offer letters, contracts, policies)     β”‚   β”‚         β”‚
β”‚  β”‚  β”‚   - Metadata: DocType, EmployeeID, ExpiryDate       β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ β€’ OnboardingTasks (Checklist items)                 β”‚   β”‚         β”‚
β”‚  β”‚  β”‚   - Task name, assigned to, due date, status        β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ β€’ Equipment (Hardware/software assignments)         β”‚   β”‚         β”‚
β”‚  β”‚  β”‚   - Laptop, monitor, phone, software licenses      β”‚   β”‚         β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚         β”‚
β”‚  β”‚                                                               β”‚         β”‚
β”‚  β”‚  Azure Functions (HTTP Triggers):                            β”‚         β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚         β”‚
β”‚  β”‚  β”‚ CreateAzureADUser    - Provision new user account    β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ AssignLicenses       - Assign M365 licenses          β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ CreateTeamsAccount   - Add to Teams channels         β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ SendWelcomeEmail     - Send welcome email with links β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ CreateCalendarEvent  - Schedule first-day meeting    β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ GenerateReport       - Create onboarding analytics   β”‚   β”‚         β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚         β”‚
β”‚  β”‚                                                               β”‚         β”‚
β”‚  β”‚  Power Automate Flows:                                       β”‚         β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚         β”‚
β”‚  β”‚  β”‚ OnboardingApproval   - Multi-stage approval chain    β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ DocumentValidation   - Check required docs uploaded  β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ TaskNotification     - Daily reminders for tasks     β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ EquipmentRequest     - Auto-create IT tickets        β”‚   β”‚         β”‚
β”‚  β”‚  β”‚ FirstDayReminder     - Email 1 day before start      β”‚   β”‚         β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚         β”‚
β”‚  β”‚                                                               β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚                                  β”‚                                          β”‚
β”‚                                  β”‚ Microsoft Graph API                      β”‚
β”‚                                  β”‚                                          β”‚
β”‚                                  β–Ό                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚                    Microsoft 365 Services                       β”‚        β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€        β”‚
β”‚  β”‚  β€’ Azure Active Directory  - User accounts & groups             β”‚        β”‚
β”‚  β”‚  β€’ Exchange Online        - Email & calendar                   β”‚        β”‚
β”‚  β”‚  β€’ Microsoft Teams        - Chat & collaboration               β”‚        β”‚
β”‚  β”‚  β€’ OneDrive               - Personal file storage              β”‚        β”‚
β”‚  β”‚  β€’ Planner                - Task management                    β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β”‚                                                                              β”‚
β”‚  Data Flow:                                                                 β”‚
β”‚  1. HR submits onboarding request via PowerApps                            β”‚
β”‚  2. Request saved to SharePoint OnboardingRequests list                    β”‚
β”‚  3. Power Automate triggers approval workflow β†’ Manager β†’ IT β†’ HR          β”‚
β”‚  4. Upon approval, Azure Function provisions Azure AD account              β”‚
β”‚  5. Azure Function assigns licenses and creates Teams membership           β”‚
β”‚  6. Documents uploaded to SharePoint library with retention policy         β”‚
β”‚  7. Task checklist generated and assigned to stakeholders                  β”‚
β”‚  8. Notifications sent via email (Graph API) for pending actions           β”‚
β”‚  9. Dashboard displays real-time progress and analytics                    β”‚
β”‚  10. On start date, automated welcome email and first-day calendar event   β”‚
β”‚                                                                              β”‚
β”‚  Security:                                                                  β”‚
β”‚  β€’ Azure AD OAuth 2.0 for all authentication                               β”‚
β”‚  β€’ Managed Identity for Azure Functions β†’ Graph API calls                  β”‚
β”‚  β€’ SharePoint permissions: HR (Full Control), Managers (Contribute)        β”‚
β”‚  β€’ Data encryption at rest (SharePoint) and in transit (TLS 1.2+)          β”‚
β”‚  β€’ Audit logs for all provisioning actions                                 β”‚
β”‚                                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Prerequisites

Required Licenses

  • Microsoft 365 E3 or E5 (for SharePoint, Teams, Power Automate)
  • Power Apps per user plan (or included in M365 E5)
  • Azure subscription (for Azure Functions - free tier sufficient for PoC)

Required Permissions

  • Global Administrator or Application Administrator (for Azure AD app registration)
  • SharePoint Administrator (for site creation and configuration)
  • Power Platform Administrator (for PowerApps environment management)
  • Azure Contributor (for creating Azure Functions)

Required Tools

  • PowerShell 7+ with modules:
    • Microsoft.Graph (for Graph API operations)
    • PnP.PowerShell (for SharePoint automation)
    • Az (for Azure Functions deployment)
  • Visual Studio Code with extensions:
    • Azure Functions
    • PowerShell
    • REST Client
  • Node.js 18+ (for Azure Functions development)
  • Git (for version control)

Verify Prerequisites

# Check PowerShell version
$PSVersionTable.PSVersion

# Install required modules
Install-Module Microsoft.Graph -Scope CurrentUser -Force
Install-Module PnP.PowerShell -Scope CurrentUser -Force
Install-Module Az -Scope CurrentUser -Force

# Verify installations
Get-Module -ListAvailable Microsoft.Graph, PnP.PowerShell, Az | 
    Select-Object Name, Version | Format-Table

# Install Azure Functions Core Tools
winget install Microsoft.Azure.FunctionsCoreTools

# Verify installation
func --version

# Install Node.js (if not already installed)
winget install OpenJS.NodeJS.LTS

# Verify Node.js
node --version
npm --version

Step 1: Create SharePoint Site and Lists

Create Onboarding Site

# Connect to SharePoint Online
$tenantUrl = "https://contoso-admin.sharepoint.com"
$siteUrl = "https://contoso.sharepoint.com/sites/Onboarding"

Connect-PnPOnline -Url $tenantUrl -Interactive

Write-Host "Creating Onboarding SharePoint site..." -ForegroundColor Cyan

# Create communication site
New-PnPSite -Type CommunicationSite `
            -Title "Employee Onboarding" `
            -Url $siteUrl `
            -Description "Central hub for employee onboarding processes" `
            -SiteDesign Blank

Write-Host "βœ“ Site created: $siteUrl" -ForegroundColor Green

# Wait for site provisioning
Start-Sleep -Seconds 10

# Connect to new site
Connect-PnPOnline -Url $siteUrl -Interactive

Create OnboardingRequests List

Write-Host "Creating OnboardingRequests list..." -ForegroundColor Cyan

# Create list
New-PnPList -Title "OnboardingRequests" `
            -Template GenericList `
            -Url "Lists/OnboardingRequests"

# Add custom columns
Add-PnPField -List "OnboardingRequests" -DisplayName "Employee First Name" `
             -InternalName "EmployeeFirstName" -Type Text -AddToDefaultView

Add-PnPField -List "OnboardingRequests" -DisplayName "Employee Last Name" `
             -InternalName "EmployeeLastName" -Type Text -AddToDefaultView

Add-PnPField -List "OnboardingRequests" -DisplayName "Employee Email" `
             -InternalName "EmployeeEmail" -Type Text -AddToDefaultView

Add-PnPField -List "OnboardingRequests" -DisplayName "Start Date" `
             -InternalName "StartDate" -Type DateTime -AddToDefaultView

Add-PnPField -List "OnboardingRequests" -DisplayName "Department" `
             -InternalName "Department" -Type Choice `
             -Choices "IT","HR","Finance","Sales","Marketing","Operations" -AddToDefaultView

Add-PnPField -List "OnboardingRequests" -DisplayName "Job Title" `
             -InternalName "JobTitle" -Type Text -AddToDefaultView

Add-PnPField -List "OnboardingRequests" -DisplayName "Manager Email" `
             -InternalName "ManagerEmail" -Type Text -AddToDefaultView

Add-PnPField -List "OnboardingRequests" -DisplayName "Status" `
             -InternalName "OnboardingStatus" -Type Choice `
             -Choices "Submitted","Manager Approved","IT Provisioned","HR Approved","Completed","Cancelled" `
             -AddToDefaultView

Add-PnPField -List "OnboardingRequests" -DisplayName "Azure AD User Created" `
             -InternalName "AzureADCreated" -Type Boolean

Add-PnPField -List "OnboardingRequests" -DisplayName "Azure AD Object ID" `
             -InternalName "AzureADObjectID" -Type Text

Add-PnPField -List "OnboardingRequests" -DisplayName "Equipment Requested" `
             -InternalName "EquipmentRequested" -Type Note

Add-PnPField -List "OnboardingRequests" -DisplayName "Special Requirements" `
             -InternalName "SpecialRequirements" -Type Note

Write-Host "βœ“ OnboardingRequests list created with custom columns" -ForegroundColor Green

Create OnboardingTasks List

Write-Host "Creating OnboardingTasks list..." -ForegroundColor Cyan

New-PnPList -Title "OnboardingTasks" -Template Tasks -Url "Lists/OnboardingTasks"

# Add custom columns
Add-PnPField -List "OnboardingTasks" -DisplayName "Onboarding Request ID" `
             -InternalName "OnboardingRequestID" -Type Number -AddToDefaultView

Add-PnPField -List "OnboardingTasks" -DisplayName "Task Category" `
             -InternalName "TaskCategory" -Type Choice `
             -Choices "Pre-Start","First Day","First Week","First Month" -AddToDefaultView

Add-PnPField -List "OnboardingTasks" -DisplayName "Assigned Department" `
             -InternalName "AssignedDepartment" -Type Choice `
             -Choices "HR","IT","Manager","Employee" -AddToDefaultView

Write-Host "βœ“ OnboardingTasks list created" -ForegroundColor Green

Create Documents Library with Metadata

Write-Host "Creating Documents library..." -ForegroundColor Cyan

# Create document library
Add-PnPDocumentLibrary -Title "OnboardingDocuments" -Url "OnboardingDocuments"

# Add metadata columns
Add-PnPField -List "OnboardingDocuments" -DisplayName "Employee Name" `
             -InternalName "EmployeeName" -Type Text -AddToDefaultView

Add-PnPField -List "OnboardingDocuments" -DisplayName "Document Type" `
             -InternalName "DocumentType" -Type Choice `
             -Choices "Offer Letter","Contract","Tax Forms","Handbook","Policy","Certificate" `
             -AddToDefaultView

Add-PnPField -List "OnboardingDocuments" -DisplayName "Onboarding Request ID" `
             -InternalName "OnboardingReqID" -Type Number -AddToDefaultView

Add-PnPField -List "OnboardingDocuments" -DisplayName "Document Status" `
             -InternalName "DocumentStatus" -Type Choice `
             -Choices "Pending Signature","Signed","Approved","Archived" -AddToDefaultView

Add-PnPField -List "OnboardingDocuments" -DisplayName "Expiry Date" `
             -InternalName "ExpiryDate" -Type DateTime

Write-Host "βœ“ OnboardingDocuments library created with metadata" -ForegroundColor Green

# Enable versioning
Set-PnPList -Identity "OnboardingDocuments" -EnableVersioning $true -MajorVersions 10

Write-Host "βœ“ Document versioning enabled (10 major versions)" -ForegroundColor Green

Seed Sample Data

Write-Host "Adding sample onboarding request..." -ForegroundColor Cyan

# Add sample request
$sampleRequest = Add-PnPListItem -List "OnboardingRequests" -Values @{
    "Title" = "Onboarding - John Smith"
    "EmployeeFirstName" = "John"
    "EmployeeLastName" = "Smith"
    "EmployeeEmail" = "john.smith@contoso.com"
    "StartDate" = (Get-Date).AddDays(7)
    "Department" = "IT"
    "JobTitle" = "Software Engineer"
    "ManagerEmail" = "jane.doe@contoso.com"
    "OnboardingStatus" = "Submitted"
    "AzureADCreated" = $false
    "EquipmentRequested" = "Laptop: Dell XPS 15, Monitor: 27-inch 4K, Keyboard, Mouse"
    "SpecialRequirements" = "Requires VPN access and access to GitHub Enterprise"
}

Write-Host "βœ“ Sample request created (ID: $($sampleRequest.Id))" -ForegroundColor Green

# Add sample tasks
$tasks = @(
    @{Title="Send welcome email"; Category="Pre-Start"; Department="HR"; DueDate=7},
    @{Title="Create Azure AD account"; Category="Pre-Start"; Department="IT"; DueDate=5},
    @{Title="Assign M365 license"; Category="Pre-Start"; Department="IT"; DueDate=5},
    @{Title="Order equipment"; Category="Pre-Start"; Department="IT"; DueDate=10},
    @{Title="Setup workstation"; Category="First Day"; Department="IT"; DueDate=1},
    @{Title="Orientation meeting"; Category="First Day"; Department="HR"; DueDate=0},
    @{Title="Team introduction"; Category="First Day"; Department="Manager"; DueDate=0},
    @{Title="Complete tax forms"; Category="First Week"; Department="Employee"; DueDate=5},
    @{Title="Review company handbook"; Category="First Week"; Department="Employee"; DueDate=7},
    @{Title="30-day check-in"; Category="First Month"; Department="Manager"; DueDate=30}
)

foreach ($task in $tasks) {
    $dueDate = (Get-Date).AddDays($task.DueDate)
    
    Add-PnPListItem -List "OnboardingTasks" -Values @{
        "Title" = $task.Title
        "OnboardingRequestID" = $sampleRequest.Id
        "TaskCategory" = $task.Category
        "AssignedDepartment" = $task.Department
        "DueDate" = $dueDate
        "Status" = "Not Started"
    }
}

Write-Host "βœ“ Sample tasks created (10 tasks)" -ForegroundColor Green

Step 2: Create Azure Function App

Create Azure Resources

# Connect to Azure
Connect-AzAccount

# Set variables
$resourceGroupName = "rg-onboarding-prod"
$location = "East US"
$storageAccountName = "stonboarding$(Get-Random -Maximum 9999)"
$functionAppName = "func-onboarding-$(Get-Random -Maximum 9999)"

Write-Host "Creating Azure resources..." -ForegroundColor Cyan

# Create resource group
New-AzResourceGroup -Name $resourceGroupName -Location $location
Write-Host "βœ“ Resource group created: $resourceGroupName" -ForegroundColor Green

# Create storage account
New-AzStorageAccount -ResourceGroupName $resourceGroupName `
                     -Name $storageAccountName `
                     -Location $location `
                     -SkuName Standard_LRS `
                     -Kind StorageV2

Write-Host "βœ“ Storage account created: $storageAccountName" -ForegroundColor Green

# Create Function App (PowerShell runtime)
New-AzFunctionApp -ResourceGroupName $resourceGroupName `
                  -Name $functionAppName `
                  -Location $location `
                  -StorageAccountName $storageAccountName `
                  -Runtime PowerShell `
                  -RuntimeVersion 7.2 `
                  -FunctionsVersion 4 `
                  -OSType Windows

Write-Host "βœ“ Function App created: $functionAppName" -ForegroundColor Green

# Enable system-assigned managed identity
Update-AzFunctionApp -ResourceGroupName $resourceGroupName `
                     -Name $functionAppName `
                     -IdentityType SystemAssigned

Write-Host "βœ“ Managed identity enabled" -ForegroundColor Green

Create Azure Function Project Locally

# Create project directory
$projectPath = "C:\Dev\OnboardingFunctions"
New-Item -ItemType Directory -Path $projectPath -Force
Set-Location $projectPath

Write-Host "Initializing Azure Functions project..." -ForegroundColor Cyan

# Initialize Functions project
func init --worker-runtime powershell

# Create HTTP-triggered functions
func new --name CreateAzureADUser --template "HTTP trigger" --authlevel "function"
func new --name AssignLicenses --template "HTTP trigger" --authlevel "function"
func new --name SendWelcomeEmail --template "HTTP trigger" --authlevel "function"

Write-Host "βœ“ Functions created: CreateAzureADUser, AssignLicenses, SendWelcomeEmail" -ForegroundColor Green

Implement CreateAzureADUser Function

# Create function code
$createUserCode = @'
using namespace System.Net

param($Request, $TriggerMetadata)

# Import Microsoft Graph module
Import-Module Microsoft.Graph.Users

# Parse request body
$requestBody = $Request.Body | ConvertFrom-Json

$firstName = $requestBody.FirstName
$lastName = $requestBody.LastName
$email = $requestBody.Email
$department = $requestBody.Department
$jobTitle = $requestBody.JobTitle

Write-Host "Creating Azure AD user: $firstName $lastName"

try {
    # Connect to Microsoft Graph using Managed Identity
    Connect-MgGraph -Identity -NoWelcome
    
    # Generate username and UPN
    $username = "$($firstName.ToLower()).$($lastName.ToLower())".Replace(" ", "")
    $upn = "$username@contoso.onmicrosoft.com"
    
    # Generate temporary password
    $password = -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 16 | ForEach-Object {[char]$_})
    $passwordProfile = @{
        Password = $password
        ForceChangePasswordNextSignIn = $true
    }
    
    # Create user parameters
    $userParams = @{
        AccountEnabled = $true
        DisplayName = "$firstName $lastName"
        GivenName = $firstName
        Surname = $lastName
        MailNickname = $username
        UserPrincipalName = $upn
        PasswordProfile = $passwordProfile
        Department = $department
        JobTitle = $jobTitle
        UsageLocation = "US"
    }
    
    # Create the user
    $newUser = New-MgUser -BodyParameter $userParams
    
    Write-Host "βœ“ User created successfully: $upn (Object ID: $($newUser.Id))"
    
    # Return success response
    $body = @{
        success = $true
        message = "User created successfully"
        userId = $newUser.Id
        upn = $upn
        temporaryPassword = $password
    } | ConvertTo-Json
    
    Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        StatusCode = [HttpStatusCode]::OK
        Body = $body
        Headers = @{ "Content-Type" = "application/json" }
    })
    
} catch {
    Write-Host "βœ— Error creating user: $($_.Exception.Message)"
    
    $errorBody = @{
        success = $false
        message = "Failed to create user"
        error = $_.Exception.Message
    } | ConvertTo-Json
    
    Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        StatusCode = [HttpStatusCode]::InternalServerError
        Body = $errorBody
        Headers = @{ "Content-Type" = "application/json" }
    })
}
'@

# Save function code
$createUserCode | Out-File -FilePath "$projectPath\CreateAzureADUser\run.ps1" -Encoding UTF8

Write-Host "βœ“ CreateAzureADUser function code saved" -ForegroundColor Green

Implement AssignLicenses Function

$assignLicensesCode = @'
using namespace System.Net

param($Request, $TriggerMetadata)

Import-Module Microsoft.Graph.Users.Actions

$requestBody = $Request.Body | ConvertFrom-Json
$userId = $requestBody.UserId
$licenses = $requestBody.Licenses  # Array of SKU IDs

Write-Host "Assigning licenses to user: $userId"

try {
    Connect-MgGraph -Identity -NoWelcome
    
    # Get available licenses
    $availableSkus = Get-MgSubscribedSku | Where-Object { 
        $_.SkuPartNumber -in @("SPE_E5", "ENTERPRISEPREMIUM", "SPB") 
    }
    
    $licensesToAssign = @()
    
    foreach ($sku in $availableSkus) {
        if ($sku.PrepaidUnits.Enabled -gt ($sku.ConsumedUnits)) {
            $licensesToAssign += @{
                SkuId = $sku.SkuId
                DisabledPlans = @()
            }
            Write-Host "  Adding license: $($sku.SkuPartNumber)"
        }
    }
    
    if ($licensesToAssign.Count -eq 0) {
        throw "No available licenses to assign"
    }
    
    # Assign licenses
    Set-MgUserLicense -UserId $userId -AddLicenses $licensesToAssign -RemoveLicenses @()
    
    Write-Host "βœ“ Licenses assigned successfully"
    
    $body = @{
        success = $true
        message = "Licenses assigned successfully"
        assignedLicenses = $licensesToAssign.Count
    } | ConvertTo-Json
    
    Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        StatusCode = [HttpStatusCode]::OK
        Body = $body
        Headers = @{ "Content-Type" = "application/json" }
    })
    
} catch {
    Write-Host "βœ— Error assigning licenses: $($_.Exception.Message)"
    
    $errorBody = @{
        success = $false
        message = "Failed to assign licenses"
        error = $_.Exception.Message
    } | ConvertTo-Json
    
    Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        StatusCode = [HttpStatusCode]::InternalServerError
        Body = $errorBody
        Headers = @{ "Content-Type" = "application/json" }
    })
}
'@

$assignLicensesCode | Out-File -FilePath "$projectPath\AssignLicenses\run.ps1" -Encoding UTF8

Write-Host "βœ“ AssignLicenses function code saved" -ForegroundColor Green

Deploy Functions to Azure

Write-Host "Deploying functions to Azure..." -ForegroundColor Cyan

# Publish to Azure
func azure functionapp publish $functionAppName

Write-Host "βœ“ Functions deployed to Azure" -ForegroundColor Green

# Get function URLs
$functionApp = Get-AzFunctionApp -ResourceGroupName $resourceGroupName -Name $functionAppName
$functionsBaseUrl = "https://$($functionApp.DefaultHostName)/api"

Write-Host "`nFunction URLs:" -ForegroundColor Yellow
Write-Host "  CreateAzureADUser: $functionsBaseUrl/CreateAzureADUser" -ForegroundColor White
Write-Host "  AssignLicenses: $functionsBaseUrl/AssignLicenses" -ForegroundColor White
Write-Host "  SendWelcomeEmail: $functionsBaseUrl/SendWelcomeEmail" -ForegroundColor White

Step 3: Configure Microsoft Graph Permissions

Grant Graph API Permissions to Managed Identity

Write-Host "Configuring Microsoft Graph API permissions..." -ForegroundColor Cyan

# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All"

# Get Function App's managed identity
$functionApp = Get-AzWebApp -ResourceGroupName $resourceGroupName -Name $functionAppName
$managedIdentityId = $functionApp.Identity.PrincipalId

Write-Host "Managed Identity Object ID: $managedIdentityId" -ForegroundColor Yellow

# Get Microsoft Graph service principal
$graphSP = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'"

# Define required permissions
$permissions = @(
    "User.ReadWrite.All",          # Create and modify users
    "Directory.ReadWrite.All",     # Read/write directory data
    "Group.ReadWrite.All",         # Manage groups
    "Mail.Send",                   # Send emails
    "Calendars.ReadWrite"          # Create calendar events
)

# Get the managed identity service principal
$msiSP = Get-MgServicePrincipal -Filter "objectId eq '$managedIdentityId'"

foreach ($permission in $permissions) {
    # Get the app role for the permission
    $appRole = $graphSP.AppRoles | Where-Object { $_.Value -eq $permission }
    
    if ($appRole) {
        try {
            # Assign the app role
            New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $msiSP.Id `
                                                     -PrincipalId $msiSP.Id `
                                                     -ResourceId $graphSP.Id `
                                                     -AppRoleId $appRole.Id
            
            Write-Host "  βœ“ Granted permission: $permission" -ForegroundColor Green
        } catch {
            if ($_.Exception.Message -like "*already exists*") {
                Write-Host "  ⚠ Permission already exists: $permission" -ForegroundColor Yellow
            } else {
                Write-Host "  βœ— Failed to grant $permission : $($_.Exception.Message)" -ForegroundColor Red
            }
        }
    }
}

Write-Host "`nβœ“ Microsoft Graph permissions configured" -ForegroundColor Green

Step 4: Create PowerApps Canvas App

Design App Screens

PowerApps Screen Structure:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Screen 1: HomeScreen
  - Welcome message
  - Navigation buttons (Create Request, View Requests, Dashboard)
  - Quick stats (Pending requests, Tasks due today)

Screen 2: CreateRequestScreen
  - Form with fields:
    * Employee First Name (Text input)
    * Employee Last Name (Text input)
    * Employee Email (Text input)
    * Start Date (Date picker)
    * Department (Dropdown)
    * Job Title (Text input)
    * Manager Email (Text input with people picker)
    * Equipment (Text area)
    * Special Requirements (Text area)
  - Submit button (saves to SharePoint list)

Screen 3: RequestListScreen
  - Gallery showing all onboarding requests
  - Filter by status (dropdown)
  - Search by employee name
  - Click request to view details

Screen 4: RequestDetailScreen
  - Request information display
  - Status progress indicator
  - Tasks checklist
  - Documents section
  - Approve/Reject buttons (for managers)
  - Provision Account button (for IT - triggers Azure Function)

Screen 5: DashboardScreen
  - Charts and metrics:
    * Requests by status (pie chart)
    * Requests by department (bar chart)
    * Average time to onboard
    * Upcoming start dates (calendar)

PowerApps Formula - OnStart (App Initialization)

// App OnStart property
ClearCollect(
    colDepartments,
    ["IT", "HR", "Finance", "Sales", "Marketing", "Operations"]
);

ClearCollect(
    colStatuses,
    ["Submitted", "Manager Approved", "IT Provisioned", "HR Approved", "Completed", "Cancelled"]
);

// Load theme colors
Set(gblThemePrimary, ColorValue("#0078D4"));      // Azure Blue
Set(gblThemeSecondary, ColorValue("#742774"));    // PowerApps Purple
Set(gblThemeSuccess, ColorValue("#107C10"));      // Green
Set(gblThemeDanger, ColorValue("#D13438"));       // Red
Set(gblThemeWarning, ColorValue("#FFB900"));      // Yellow
Set(gblBackground, ColorValue("#F3F2F1"));        // Light gray
Set(gblTextPrimary, ColorValue("#201F1E"));       // Dark gray

// Get current user
Set(gblCurrentUser, User());

// Load data from SharePoint
ClearCollect(
    colRequests,
    'OnboardingRequests'
);

ClearCollect(
    colTasks,
    'OnboardingTasks'
);

// Calculate stats
Set(
    gblPendingCount,
    CountRows(Filter(colRequests, OnboardingStatus <> "Completed" && OnboardingStatus <> "Cancelled"))
);

Set(
    gblTasksDueToday,
    CountRows(Filter(colTasks, DateDiff(Today(), DueDate) = 0 && Status <> "Completed"))
);

CreateRequestScreen - Submit Button Formula

// btnSubmit OnSelect property
If(
    // Validation
    IsBlank(txtFirstName.Text) || IsBlank(txtLastName.Text) || IsBlank(txtEmail.Text),
    Notify("Please fill in all required fields", NotificationType.Error),
    
    // Submit to SharePoint
    Patch(
        'OnboardingRequests',
        Defaults('OnboardingRequests'),
        {
            Title: Concatenate(txtFirstName.Text, " ", txtLastName.Text, " - Onboarding"),
            EmployeeFirstName: txtFirstName.Text,
            EmployeeLastName: txtLastName.Text,
            EmployeeEmail: txtEmail.Text,
            StartDate: datStartDate.SelectedDate,
            Department: ddDepartment.Selected.Value,
            JobTitle: txtJobTitle.Text,
            ManagerEmail: txtManagerEmail.Text,
            OnboardingStatus: "Submitted",
            AzureADCreated: false,
            EquipmentRequested: txtEquipment.Text,
            SpecialRequirements: txtSpecialReqs.Text
        }
    );
    
    // Show success message
    Notify("Onboarding request submitted successfully!", NotificationType.Success);
    
    // Refresh data
    Refresh('OnboardingRequests');
    ClearCollect(colRequests, 'OnboardingRequests');
    
    // Navigate back
    Navigate(RequestListScreen, ScreenTransition.Fade);
    
    // Clear form
    Reset(txtFirstName);
    Reset(txtLastName);
    Reset(txtEmail);
    Reset(datStartDate);
    Reset(ddDepartment);
    Reset(txtJobTitle);
    Reset(txtManagerEmail);
    Reset(txtEquipment);
    Reset(txtSpecialReqs);
)

RequestDetailScreen - Provision Account Button

// btnProvisionAccount OnSelect property
Set(varIsProvisioning, true);

// Call Azure Function to create Azure AD user
Set(
    varCreateUserResponse,
    ParseJSON(
        CreateAzureADUser.Run(
            JSON(
                {
                    FirstName: galRequests.Selected.EmployeeFirstName,
                    LastName: galRequests.Selected.EmployeeLastName,
                    Email: galRequests.Selected.EmployeeEmail,
                    Department: galRequests.Selected.Department,
                    JobTitle: galRequests.Selected.JobTitle
                }
            )
        ).Body
    )
);

If(
    varCreateUserResponse.success,
    
    // User created successfully
    Concurrent(
        // Update SharePoint list
        Patch(
            'OnboardingRequests',
            galRequests.Selected,
            {
                AzureADCreated: true,
                AzureADObjectID: varCreateUserResponse.userId,
                OnboardingStatus: "IT Provisioned"
            }
        ),
        
        // Call Azure Function to assign licenses
        Set(
            varLicenseResponse,
            AssignLicenses.Run(
                JSON(
                    {
                        UserId: varCreateUserResponse.userId,
                        Licenses: ["SPE_E5"]
                    }
                )
            )
        ),
        
        // Send welcome email
        Set(
            varEmailResponse,
            SendWelcomeEmail.Run(
                JSON(
                    {
                        ToEmail: galRequests.Selected.EmployeeEmail,
                        EmployeeName: Concatenate(galRequests.Selected.EmployeeFirstName, " ", galRequests.Selected.EmployeeLastName),
                        StartDate: Text(galRequests.Selected.StartDate, "mm/dd/yyyy"),
                        TemporaryPassword: varCreateUserResponse.temporaryPassword
                    }
                )
            )
        )
    );
    
    // Show success
    Notify("Account provisioned successfully! Welcome email sent.", NotificationType.Success);
    
    // Refresh data
    Refresh('OnboardingRequests'),
    
    // Show error
    Notify(Concatenate("Failed to create account: ", varCreateUserResponse.message), NotificationType.Error)
);

Set(varIsProvisioning, false);

DashboardScreen - Status Chart

// chartRequestsByStatus Items property
AddColumns(
    GroupBy(
        Filter(colRequests, OnboardingStatus <> "Cancelled"),
        "OnboardingStatus",
        "Count"
    ),
    "Total",
    CountRows(Count)
)

// chartRequestsByStatus Legend
OnboardingStatus

// chartRequestsByStatus Value
Total

// Gallery showing upcoming start dates
SortByColumns(
    Filter(
        colRequests,
        StartDate >= Today() && StartDate <= DateAdd(Today(), 14) && OnboardingStatus <> "Completed"
    ),
    "StartDate",
    Ascending
)

Step 5: Create Power Automate Approval Workflow

Create Approval Flow

Flow Name: Onboarding Request Approval
Trigger: When an item is created (SharePoint - OnboardingRequests)

Actions:

1. Get Manager Details
   - Input: Manager Email from request
   - Output: Manager display name, email

2. Start and wait for an approval
   - Approval type: Approve/Reject - First to respond
   - Title: "New Employee Onboarding Request"
   - Assigned to: Manager Email
   - Details: 
     Employee: {EmployeeFirstName} {EmployeeLastName}
     Start Date: {StartDate}
     Department: {Department}
     Job Title: {JobTitle}
     Equipment: {EquipmentRequested}
     
3. Condition: If approval outcome = Approved
   
   YES Branch:
   
   4a. Update item (SharePoint)
      - Status = "Manager Approved"
      
   4b. Send email notification to HR
      - To: hr@contoso.com
      - Subject: "Onboarding Approved - {EmployeeFirstName} {EmployeeLastName}"
      - Body: Manager has approved the onboarding request. Please proceed with next steps.
      
   4c. Send email to IT
      - To: it@contoso.com
      - Subject: "New Employee Equipment Request"
      - Body: Equipment needed for {EmployeeFirstName} {EmployeeLastName} starting {StartDate}
            {EquipmentRequested}
            
   4d. Create planner task
      - Plan: "HR Operations"
      - Bucket: "Onboarding"
      - Title: "Complete onboarding for {EmployeeFirstName} {EmployeeLastName}"
      - Due date: {StartDate}
      - Assigned to: HR team
   
   NO Branch:
   
   5a. Update item (SharePoint)
      - Status = "Cancelled"
      
   5b. Send email notification to HR
      - To: hr@contoso.com
      - Subject: "Onboarding Rejected - {EmployeeFirstName} {EmployeeLastName}"
      - Body: Manager has rejected the onboarding request. Reason: {Approval comments}

PowerShell Script to Export/Import Flow

# Export flow definition
Install-Module -Name Microsoft.PowerApps.Administration.PowerShell

# Connect to Power Platform
Add-PowerAppsAccount

# List environments
Get-AdminPowerAppEnvironment

# Set environment
$environmentName = "Default-12345678-1234-1234-1234-123456789012"

# List flows in environment
Get-AdminFlow -EnvironmentName $environmentName | 
    Select-Object FlowName, DisplayName, CreatedTime | 
    Format-Table

# Export specific flow
$flowName = "12345678-1234-1234-1234-123456789012"
$flow = Get-AdminFlow -EnvironmentName $environmentName -FlowName $flowName

$flow | ConvertTo-Json -Depth 10 | Out-File "C:\Temp\OnboardingApprovalFlow.json"

Write-Host "βœ“ Flow exported to C:\Temp\OnboardingApprovalFlow.json" -ForegroundColor Green

Step 6: Testing and Validation

End-to-End Test Script

Write-Host "`n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
Write-Host "Onboarding Portal - End-to-End Test" -ForegroundColor Cyan
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`n" -ForegroundColor Cyan

# Connect to SharePoint
Connect-PnPOnline -Url $siteUrl -Interactive

# Test 1: Create onboarding request
Write-Host "Test 1: Creating onboarding request..." -ForegroundColor Yellow

$testRequest = Add-PnPListItem -List "OnboardingRequests" -Values @{
    "Title" = "TEST - Jane Wilson"
    "EmployeeFirstName" = "Jane"
    "EmployeeLastName" = "Wilson"
    "EmployeeEmail" = "jane.wilson@contoso.com"
    "StartDate" = (Get-Date).AddDays(14)
    "Department" = "Finance"
    "JobTitle" = "Financial Analyst"
    "ManagerEmail" = "john.doe@contoso.com"
    "OnboardingStatus" = "Submitted"
    "AzureADCreated" = $false
    "EquipmentRequested" = "Laptop, Monitor, Mouse, Keyboard"
}

Write-Host "  βœ“ Request created (ID: $($testRequest.Id))" -ForegroundColor Green

# Test 2: Verify Power Automate trigger
Write-Host "`nTest 2: Verifying Power Automate trigger..." -ForegroundColor Yellow
Write-Host "  ⚠ Check Power Automate run history for approval request" -ForegroundColor Yellow
Write-Host "  ⚠ Manager should receive approval email" -ForegroundColor Yellow

Start-Sleep -Seconds 5

# Test 3: Simulate approval (update status)
Write-Host "`nTest 3: Simulating manager approval..." -ForegroundColor Yellow

Set-PnPListItem -List "OnboardingRequests" -Identity $testRequest.Id -Values @{
    "OnboardingStatus" = "Manager Approved"
}

Write-Host "  βœ“ Status updated to Manager Approved" -ForegroundColor Green

# Test 4: Call Azure Function to create user
Write-Host "`nTest 4: Calling Azure Function to create Azure AD user..." -ForegroundColor Yellow

$functionUrl = "$functionsBaseUrl/CreateAzureADUser"
$functionKey = (Get-AzFunctionAppSetting -ResourceGroupName $resourceGroupName -Name $functionAppName).Values['FUNCTIONS_EXTENSION_VERSION']

$body = @{
    FirstName = "Jane"
    LastName = "Wilson"
    Email = "jane.wilson@contoso.com"
    Department = "Finance"
    JobTitle = "Financial Analyst"
} | ConvertTo-Json

try {
    $response = Invoke-RestMethod -Uri "$functionUrl?code=<YOUR_FUNCTION_KEY>" `
                                   -Method Post `
                                   -Body $body `
                                   -ContentType "application/json"
    
    if ($response.success) {
        Write-Host "  βœ“ User created: $($response.upn)" -ForegroundColor Green
        Write-Host "  βœ“ Object ID: $($response.userId)" -ForegroundColor Green
        Write-Host "  βœ“ Temporary Password: $($response.temporaryPassword)" -ForegroundColor Green
        
        # Update SharePoint
        Set-PnPListItem -List "OnboardingRequests" -Identity $testRequest.Id -Values @{
            "AzureADCreated" = $true
            "AzureADObjectID" = $response.userId
            "OnboardingStatus" = "IT Provisioned"
        }
        
        Write-Host "  βœ“ SharePoint updated with Azure AD info" -ForegroundColor Green
    }
    
} catch {
    Write-Host "  βœ— Function call failed: $($_.Exception.Message)" -ForegroundColor Red
}

# Test 5: Verify tasks created
Write-Host "`nTest 5: Verifying tasks..." -ForegroundColor Yellow

$tasks = Get-PnPListItem -List "OnboardingTasks" -Query "<View><Query><Where><Eq><FieldRef Name='OnboardingRequestID'/><Value Type='Number'>$($testRequest.Id)</Value></Eq></Where></Query></View>"

Write-Host "  βœ“ Found $($tasks.Count) tasks" -ForegroundColor Green

# Test 6: Verify documents library
Write-Host "`nTest 6: Checking documents library..." -ForegroundColor Yellow

$docs = Get-PnPListItem -List "OnboardingDocuments"
Write-Host "  β„Ή Total documents: $($docs.Count)" -ForegroundColor Cyan

Write-Host "`n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Green
Write-Host "End-to-End Test Completed!" -ForegroundColor Green
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`n" -ForegroundColor Green

# Cleanup
Write-Host "Cleanup: Removing test request? (Y/N)" -ForegroundColor Yellow
$cleanup = Read-Host

if ($cleanup -eq "Y") {
    Remove-PnPListItem -List "OnboardingRequests" -Identity $testRequest.Id -Force
    Write-Host "βœ“ Test request deleted" -ForegroundColor Green
}

Real-World Enterprise Example: Contoso Corporation

Scenario: Contoso Corporation (5,000 employees) onboards 50-100 new employees per month across multiple departments and locations.

Before Implementation

  • Manual process: 2 weeks average onboarding time
  • Email-based: 200+ emails per onboarding
  • Error rate: 15% of accounts had provisioning errors
  • Document chaos: Lost forms, missing signatures
  • No visibility: Managers had no visibility into onboarding status

After Implementation

  • Automated workflow: 3 days average onboarding time
  • Centralized portal: Single source of truth
  • Error reduction: 2% error rate (90% improvement)
  • Digital documents: 100% paperless with retention policies
  • Real-time dashboard: Managers see progress instantly

Metrics After 6 Months

Metric Before After Improvement
Average Onboarding Time 14 days 3 days 79% faster
HR Time per Onboarding 8 hours 2 hours 75% reduction
IT Time per Onboarding 4 hours 30 min 87% reduction
Provisioning Errors 15% 2% 87% reduction
New Hire Satisfaction 3.2/5 4.7/5 47% increase
Cost per Onboarding $450 $120 73% reduction

Success Stories

IT Department: "We reduced new hire provisioning from 4 hours to 30 minutes. Azure Functions handle everything automaticallyβ€”user creation, license assignment, group membership. No more manual work."

HR Team: "The dashboard gives us complete visibility. We can see exactly where each onboarding is in the process. Managers love the approval workflowβ€”it takes them 2 minutes instead of scheduling meetings."

New Employees: "I received everything I needed before my first day. My laptop was ready, accounts were set up, and I had a personalized welcome email with all the information. It felt professional and organized."

Best Practices Summary

DO:

  1. βœ… Use managed identities for Azure Functions (never hard-code credentials)
  2. βœ… Implement approval workflows for compliance
  3. βœ… Enable versioning on document libraries
  4. βœ… Use metadata for document organization
  5. βœ… Create mobile-responsive PowerApps interfaces
  6. βœ… Implement error handling in all Azure Functions
  7. βœ… Use concurrent loading in PowerApps for performance
  8. βœ… Set up monitoring and alerts for Azure Functions
  9. βœ… Document the solution architecture
  10. βœ… Test end-to-end before production deployment

DON'T:

  1. ❌ Store sensitive data in PowerApps collections
  2. ❌ Skip input validation in forms
  3. ❌ Hard-code tenant-specific values
  4. ❌ Forget to handle Power Automate flow failures
  5. ❌ Skip permission testing for different user roles
  6. ❌ Deploy without backup/rollback plan
  7. ❌ Ignore Azure Function timeout limits (5 min default)
  8. ❌ Use admin accounts for service connections
  9. ❌ Skip load testing for high-volume scenarios
  10. ❌ Forget to train end users

Key Takeaways

  1. Integration is powerful - Azure + PowerApps + SharePoint creates enterprise solutions
  2. Serverless scales - Azure Functions handle automation without infrastructure management
  3. Low-code accelerates - PowerApps delivers professional UIs in hours, not weeks
  4. Workflows eliminate email - Power Automate replaces email chains with structured approvals
  5. Managed Identity = Security - No credentials in code, Azure handles authentication
  6. Metadata enables search - SharePoint metadata makes documents discoverable
  7. Mobile-first matters - 40% of onboarding accessed from mobile devices
  8. Dashboards drive decisions - Real-time visibility improves process efficiency
  9. Testing prevents issues - End-to-end testing catches integration problems
  10. Documentation ensures success - Architecture docs help future maintenance

Additional Resources

Next Steps

  1. Add Microsoft Teams integration: Post onboarding updates to Teams channels
  2. Implement analytics: Track onboarding metrics with Power BI
  3. Create offboarding workflow: Reverse process for departing employees
  4. Add equipment tracking: Integrate with asset management system
  5. Implement self-service: Allow employees to update their own information
  6. Add compliance reporting: Generate audit reports for HR compliance
  7. Create mobile app: Publish PowerApps as mobile app for app stores
  8. Integrate with HRIS: Sync with existing HR systems (Workday, SAP SuccessFactors)

Ready to transform your onboarding process? Start with SharePoint lists and Azure Functions, then layer on PowerApps for the UIβ€”this architecture scales from 10 employees to 10,000!