Building a Secure LAPS Password Portal with Azure and Microsoft Graph

What You Will Build

  • An App registration (backend) to call Microsoft Graph with the correct permissions.
  • A Resource group for all your Azure resources.
  • An Azure Function that queries Microsoft Graph for LAPS information with the App registration.
  • A Log Analytics workspace so every lookup can be tracked by user.
  • An Azure Web App that hosts a simple password and LAPS account lookup interface.
  • An App Service Authentication so only authorized users can access the portal

You need an active Azure subscription with Owner permissions in order to realise the LAPS portal.


Step 1: Create the App Registration for the Backend

This app registration is used by the Azure Function to call Microsoft Graph.

Go to Entra IDApp registrations → + New registration

  • Name: view laps data
  • Supported account types: Single tenant
  • Redirect URI (optional): leave as is.
  • Click Register

Go to Certificates & secrets+ New client secret → Enter a description: LAPS-data-secret and set the Expires to: Recommended: 180 days → Add

Copy the Value and keep it save you will need it later in step 4.

Go to API permissions → + Add a permissionMicrosoft GraphApplication permissions → Add:

  • Device.Read.All
  • DeviceLocalCredential.Read.All

Remove permissions User.Read. Click Grant admin consent for …

Click Yes.

Step 2: Create the Resource Group

Go to Resource groups → + Create

  • Subscription: select your subscription
  • Name: rg-laps-data-portal (the name you prefer)
  • Region: West Europe (your desired region)
  • Click Review + create → Create

Step 3: Create the Log Analytics Workspace

Go to Log Analytics workspaces → + Create

  • Resource group: rg-laps-data-portal (the RG your create before)
  • Name: law-laps-data-portal (name you prefer)
  • Region: West Europe (your desired region)
  • Click Review + create → Create

Once created, open Azure Cloud Shell

Choose PowerShell when asked.

Run the following script to retrieve the Workspace ID and Primary key:

$law = Get-AzOperationalInsightsWorkspace -ResourceGroupName "rg-laps-data-portal" -Name "law-laps-data-portal"; Write-Host "Workspace ID: $($law.CustomerId)"; Write-Host "Primary Key: $((Get-AzOperationalInsightsWorkspaceSharedKey -ResourceGroupName "rg-laps-data-portal" -Name "law-laps-data-portal").PrimarySharedKey)"

Note both output values — you will need them in Step 4 for the Function App environment variables

Step 4: Create the Azure Function App

Go to Function App → +Create

  • Select hosting option: Consumption (Windows) and Confirm.
  • Resource group: rg-laps-data-portal (from step 2)
  • Function App name: laps-Graph-portal (name you prefer)
  • Runtime stack: PowerShell Core
  • Version: 7.4
  • Region: West Europe (your desired region)
  • Click Review + create → Create

Click: Go to resource.


SettingsEnvironment variablesApp settings+ add:

NameValue
LAPS_TENANT_IDYour tenant ID
LAPS_CLIENT_IDClient ID from step 1
LAPS_CLIENT_SECRETClient secret from Step 1
LAW_WORKSPACE_IDWorkspace ID from Step 3
LAW_PRIMARY_KEYPrimary key from Step 3

After adding the variables click: ApplyConfirm

Go to the Overview → Create in Azure portal.

Select: HTTP trigger → Next.

  • Name: GetLapsPassword
  • Authorization level: Function → Create

In Code + Test → replace the run.ps1 content with the content of the Function script from the GitHub repo and click Save.

Click Get Function URL → copy the URL including the function key, we will need this in step 7.

Step 5: Create the Web App

Go to App ServicesCreate → Web App

  • Resource group: rg-laps-data-portal (from step 2)
  • Name: laps-data-portal (name you prefer)
  • Runtime stack: PHP 8.5
  • OS: Linux
  • Region: West Europe (your desired region)
  • Linux plan: Create new: ASP-LAPS-data-portal → OK
  • Pricing plan: Free F1 (free)
  • Click Review + create → Create

Click go to resource.

Note/copy the URL from Default domain on the Overview page — you need this in the next step.

Step 6: Create the App Registration for the Frontend

This app registration is required before you can enable App Service Authentication in Step 8. You need the Web App URL from Step 5 to complete this step.

Go to Entra IDApp registrations → + New registration

  • Name: LAPS-Portal-frontend (name your prefer)
  • Supported account types: Single tenant only
  • Click Register

Note the Application (client) ID — you will need this in Step 8

Go to Manage → Authentication (Preview)+ Add Redirect URI

Choose Web (not SPA)

  • Enter Redirect URI: https://<default-domein-url-from-step-5>/.auth/login/aad/callback
  • Check ID tokens
  • Click Configure.

Grant admin consent to prevent permission prompts

To ensure no users in your tenant see a permissions consent prompt when opening the portal:

Go to Permissions → click Grant admin consent for <your tenant>

Click Yes.

Remove the permission:

Step 7: Deploy the Frontend

Go to App services → Open the web app your created in step 5 → Development ToolsAdvanced ToolsGo → (opens Kudu)

Open File Manager → navigate to site/wwwroot

Drag and drop index.html and proxy.php from the GitHub repo into the file manager and delete hostingstart.html.

Correct upload:

Wrong upload:

proxy.php acts as a server-side proxy between the browser and the Azure Function. The real Function URL and key are stored as a Web App environment variable (FUNCTION_URL) and never exposed to the browser.

Add the FUNCTION_URL environment variable to the Web App

Go to the Web App (laps-data-portal) → SettingsEnvironment variablesApp settings+ Add:

NameValue
FUNCTION_URLhttps://<your-function-app>.azurewebsites.net/api/GetLapsPassword?code=<your-function-key-from-step-4>

Click ApplyApplyConfirm

You can now test the laps portal by browsing to the Default domain URL from step 4:

Enter an Intune devicename and click Retrieve to check the LAPS data.

Step 8: Enable App Service Authentication on the Web App

Now that the LAPS portal works we want to enable authentication before anyone can access the LAPS portal web app.

Go to the Web App (laps-data-portal) → Settings → AuthenticationAdd identity provider

Choose Microsoft

App registration type: Pick an existing app registration in this directory

Select LAPS-Portal-frontend from Step 6

Client secret expiration: 180 days

Unauthenticated requests: HTTP 302 found redirect:…

Click Add

All you tenant users are now automatically redirected to Microsoft login before they (all tenant users) can access the portal, which is a bit much.

Step 9: Restrict Access to Specific Users

To limit access to the LAPS Portal we can assign access to a specific Entra ID group with (IT support) members.

Go to Entra IDEnterprise ApplicationsLAPS-Portal-frontend (From step 6)

Properties → set Assignment required to YesSave

Users and groupsAdd user/group → assign your admin group

Now users NOT a member of the added group will see this when logging in the LAPS portal web app:

Optional: Enforce MFA and Session Timeout

To require MFA (Microsoft Authenticator) and automatically sign out users after 30 minutes of inactivity, configure a Conditional Access policy:

Go to Entra IDSecurityProtectConditional Access+ Create New policy

Name: LAPS Portal - Require MFA and Session

Users: select the group that has access to the portal (Step 9)

Target resourcesSelect resources → select LAPS-Portal-frontend

Grant → select Grant access → Require authentication strength → Select Multifactor authentication, click Select.

SessionSign-in frequency → Periodic reauthentication → 1 Hour

Enable policy: On

Click Create.

Now user who sign in the LAPS Portal must complete a MFA step.

View the Audit Log

Every lookup is logged to Log Analytics with the user’s UPN, device name, IP address, and result. To query the log:

  1. Go to your Log Analytics WorkspaceLogs
  2. Run this KQL query:
LAPSPortal_CL
| extend LocalTime = TimeGenerated + 2h
| project LocalTime, CallerUPN_s, DeviceName_s, AccountName_s, CallerIP_s, Result_s
| order by LocalTime desc

+ 2h converts UTC to Central European Summer Time (CEST). Use + 1h in winter (CET).

It can take up to 30 min. after the first retrieve from the portal before the log query returns data.

Save the query via SaveSave as query so you don’t have to retype it. You can also pin the results to your Azure dashboard via Pin toAzure dashboard for quick daily access.

Conclusion

With just a few Azure resources and no custom authentication code, you now have a secure, mobile-friendly LAPS portal that any authorized helpdesk engineer or IT administrator can use from anywhere — whether they’re at their desk or standing next to a device in the field.

No more navigating through the Intune portal to find a LAPS password. Just open the portal, type the device name, and you’re done.

A few things to take away from this build:

  • App Service Authentication secures the portal without writing any authentication code — Azure handles the entire login flow automatically.
  • Store secrets as Function App environment variables, never hardcoded in scripts — this keeps sensitive credentials out of your source code and version control.
  • Every lookup is logged with the caller’s UPN, device name, IP address, and result — giving you full accountability and a clear audit trail of who accessed what and when.
  • Don’t forget to configure Conditional Access with MFA and a session timeout — this adds an important extra layer of security on top of the portal authentication.
  • The first start/sign in to the LAPS portal can take a bit longer. When the Function App has been idle for a while, Azure deallocates the underlying infrastructure to save resources. The first request after a period of inactivity forces Azure to spin up the PowerShell runtime again, which takes 10–30 seconds.

The total Azure cost for normal internal use is effectively zero — the Function App runs on a Consumption plan and the Web App on a free F1 tier.

Flowchart

So I put all the created components and their relationships into a flowchart.

Click any component to learn more about its role

Browser User opens LAPS Portal login redirect App Registration LAPS-Portal-frontend session token Conditional Access MFA + 60 min timeout Azure Web App index.html + proxy.php 🔒 FUNCTION_URL proxy.php Function App GetLapsPassword 🔒 LAPS_CLIENT_SECRET 🔒 LAW_WORKSPACE_ID · LAW_PRIMARY_KEY App Registration view laps data (backend) client ID + secret Graph API call Microsoft Graph /deviceLocalCredentials audit log Log Analytics law-laps-data-portal Legend Main data flow Auth / supporting 🔒 VAR Encrypted environment variable
🌐
Browser
User opens LAPS Portal
login redirect via LAPS-Portal-frontend
🔐
App Registration – LAPS-Portal-frontend
Frontend / Easy Auth
🛡️
Conditional Access
MFA + 30 min timeout
session token — portal loads
🖥️
Azure Web App
index.html + proxy.php · 🔒 FUNCTION_URL
proxy.php
Function App
🔒 LAPS_CLIENT_SECRET · LAW_WORKSPACE_ID · LAW_PRIMARY_KEY
🔑
App Registration – view laps data
client ID + secret
Graph API call
📊
Microsoft Graph
/deviceLocalCredentials
audit log
📋
Log Analytics
law-laps-data-portal

Theme: Overlay by Kaira