Admin Guide¶
This guide walks administrators through setting up and managing the App Store for Intune using the web-based admin interface.
Getting Started¶
After initial deployment and Entra ID configuration (see SETUP.md), most portal configuration can be done directly through the Admin Dashboard.
Accessing the Admin Dashboard¶
- Sign in to the portal with an account that is a member of the Admin Group
- Click Admin in the navigation menu
- The admin portal uses an Intune-style left-rail navigation (v1.28.0+) grouped into four sections that mirror the Microsoft Intune admin center layout. If you've used Intune, the structure should already feel familiar:
Apps
- App Management — Manage apps synced from Intune (visibility, approval workflows, deployment options, version rollback)
- App Catalog — Browse the public WinGet repository and publish apps to Intune
- App Upload (v1.29.0+) — Upload custom .msi installers for apps that aren't in WinGet
- App Updates — Track and deploy update versions for published apps with ring-based rollouts
Approvals - Pending Approvals — Review and approve/reject requests
Reports - Analytics — Request volume, approval throughput, install outcomes, ROI calculator - Store Health — Operational metrics for the portal itself (request volume, deploy pipeline, error rates, latency) - Activity Log — Full timeline of packaging jobs and update deployments
Store Administration - Settings — Authorization, deployment defaults, automation policies, WinGet integration - Branding — Logo, favicon, brand colors, footer - Communications — Company info, notification recipients, Terms of Service, per-event toggles - License — PowerStacks license activation and entitlement status
Setup Wizard¶
For first-time setup or to reconfigure the portal, use the Setup Wizard:
- Go to Admin > Settings tab
- Click the 🚀 Setup Wizard button
- Follow the guided steps:
- Welcome - Overview of setup steps
- License - Enter and validate your PowerStacks license key
- Access Groups - Configure Admin and Approver Entra ID groups
- Email Notifications - Set up email settings for notifications
- Sync Apps - Import apps from your Intune tenant
The wizard saves your settings as you progress through each step. You can skip the wizard at any time and configure settings manually.
Note: The License step is required for the portal to be fully operational. Without a valid license, users will see warning banners and some features may be restricted.
Quick Reference Commands (shown in wizard completion):
| Component | Command |
|-----------|---------|
| API (includes packaging service) | cd src/AppRequestPortal.API && dotnet run |
| Web | cd src/AppRequestPortal.Web && npm start |
Portal Settings¶
The Settings tab allows you to configure portal-wide options including authorization, display settings, deployment configuration, and version management. Notification and messaging settings are on the Communications tab (see below).
Group-Based Authorization¶
Control who has admin and approver access to the portal.
| Setting | Description |
|---|---|
| Admin Group | (Required) Entra ID group Object ID. Members have full admin access to sync apps, manage settings, and view all requests. If not configured, all admin endpoints return 403 Forbidden. |
| Approver Group | Entra ID group Object ID. Members can approve/reject requests (in addition to workflow-specific approvers) |
Important (v1.10.6+): The Admin Group is required. If no Admin Group ID is configured (in either portal settings or
appsettings.json), all users are denied admin access. See SETUP.md for initial configuration instructions.Lost admin access? If the Admin Group ID is accidentally cleared from portal settings, the
appsettings.json/ environment variable value is used as a fallback. If neither is set, you must setAppSettings__AdminGroupIdas an environment variable (or inappsettings.json) and restart the application to regain access.
Recommended Conditional Access Policy¶
Since the App Store for Intune is used to request apps for Intune-managed devices, we recommend protecting access to the portal with a Conditional Access policy that requires: - Managed device - The device accessing the portal must be enrolled in Intune - Compliant device - The device must meet your organization's compliance policies
This ensures users can only request apps from trusted, compliant devices.
Prerequisites¶
Before creating the policy: 1. You must have Entra ID Premium P1 or P2 license (or Microsoft 365 E3/E5, etc.) 2. You need the Conditional Access Administrator or Global Administrator role 3. Have at least one compliance policy configured in Intune
Creating the Conditional Access Policy¶
- Navigate to Conditional Access
- Go to Azure Portal
- Navigate to Microsoft Entra ID > Security > Conditional Access
-
Click + New policy
-
Name the Policy
-
Enter a descriptive name:
App Store for Intune - Require Compliant Device -
Configure Assignments - Users
- Under Users, click 0 users and groups selected
- Select Include > All users
-
(Optional) Under Exclude, add a break-glass admin account for emergency access
-
Configure Assignments - Target Resources
- Under Target resources, click No target resources selected
- Select Cloud apps
- Click Include > Select apps
- Search for and select your App Store for Intune app registrations:
App Store for Intune API(or your API app registration name)App Store for Intune Frontend(or your frontend app registration name)
-
Click Select
-
Configure Conditions (Optional)
- Under Conditions > Device platforms
- Click Not configured
- Set Configure to Yes
- Select Include > Select device platforms
- Check: Windows, iOS, Android (the platforms you manage)
-
Click Done
-
Configure Access Controls - Grant
- Under Grant, click 0 controls selected
- Select Grant access
- Check Require device to be marked as compliant
- Check Require Microsoft Entra hybrid joined device (optional, for hybrid environments)
- Select Require one of the selected controls (OR) or Require all the selected controls (AND) based on your requirements
-
Click Select
-
Configure Session Controls (Optional)
-
Under Session, you can configure:
- Sign-in frequency: Require re-authentication periodically
- Persistent browser session: Disable persistent sessions for extra security
-
Enable the Policy
- Set Enable policy to Report-only first to test
-
Click Create
-
Test and Enable
- Monitor the Sign-in logs for a few days in Report-only mode
- Verify legitimate users can access the portal from compliant devices
- Verify access is blocked from non-compliant/unmanaged devices
- Once verified, edit the policy and change to On
Policy Summary¶
| Setting | Value |
|---|---|
| Name | App Store for Intune - Require Compliant Device |
| Users | All users (exclude break-glass account) |
| Cloud apps | App Store for Intune API, App Store for Intune Frontend |
| Conditions | Device platforms: Windows, iOS, Android |
| Grant | Require device to be marked as compliant |
| Enable policy | Report-only (then On after testing) |
Troubleshooting Access Issues¶
If users report they cannot access the portal:
- Check Sign-in Logs
- Go to Microsoft Entra ID > Sign-in logs
- Filter by the user and application
- Look for Failure entries and check the Conditional Access tab
-
The tab shows which policies applied and why access was denied
-
Common Issues | Issue | Solution | |-------|----------| | Device not enrolled | User needs to enroll their device in Intune | | Device not compliant | User needs to resolve compliance issues (updates, encryption, etc.) | | Using personal device | User needs to use their work-managed device | | Policy excluding wrong users | Review the Exclude settings in the CA policy |
-
Verify Device Status
- Go to Microsoft Intune admin center > Devices
- Search for the user's device
- Check Compliance status and any failed compliance policies
Alternative: Allow Browser Access with App Protection¶
If you need to allow browser access from unmanaged devices (less secure), you can create an alternative policy:
- Create a second CA policy for browser access
- Target the same apps
- Under Conditions > Client apps, select Browser only
- Under Grant, require Approved client app or App protection policy
- This allows access from unmanaged devices but with some protection
Recommendation: For maximum security, require compliant managed devices. The App Store for Intune is designed for employees requesting apps on their managed devices, so this policy aligns with the intended use case.
General Settings¶
| Setting | Description |
|---|---|
| Require manager approval by default | When enabled, new approval workflows include manager approval as the first stage |
| Auto-create Entra ID groups | Automatically create a security group when an app doesn't have a target group configured |
Version & Updates¶
The Settings tab displays version information and update settings:
| Setting | Description |
|---|---|
| Current Version | Displays the installed portal version, build date, and environment |
| Automatically check for updates | When enabled, the portal periodically checks for new versions |
| Show update notifications | When enabled, displays a notification banner when updates are available |
| Check for Updates | Manual button to check for available updates |
| Install Update | One-click button to download and install updates (requires configuration) |
When an update is available, you'll see: - Update badge with the new version number - Link to release notes - Install Update button (if auto-update is configured)
Enabling In-App Updates¶
The portal supports one-click updates directly from the Admin Dashboard. This feature downloads the latest release and deploys it via Azure's Kudu ZIP deploy API.
Prerequisites: - Portal must be running in Azure App Service - Deployment credentials must be configured
Configuration Steps:
- Get Deployment Credentials from Azure Portal:
- Go to your App Service → Deployment Center → FTPS credentials
- Copy the Username (starts with
$, e.g.,$app-apprequest-prod-abc123) -
Copy the Password
-
Add App Settings in Azure Portal:
- Go to your App Service → Configuration → Application settings
- Add these settings:
| Name | Value |
|---|---|
Deployment__PublishUser |
Your FTPS username (e.g., $app-apprequest-prod-abc123) |
Deployment__PublishPassword |
Your FTPS password |
- Using the Update Feature:
- Go to Admin > Settings > Version & Updates
- Click Check for Updates to see if a new version is available
- If configured correctly, an Install Update button appears
- Click to download and deploy the update automatically
- The application will restart during the update process
Note: The Install Update button only appears when: - The portal is running in Azure App Service (not locally) - Deployment credentials are properly configured - An update is available
Manual Updates¶
If auto-update is not configured, you can manually update using either method below:
Method 1: Kudu ZIP Deploy (Recommended for existing installations)
For existing deployments, use the Kudu ZIP deployment feature:
- Go to the releases repository
- Download the latest
AppRequestPortal-X.X.X.zipfile (not the source code) - In Azure Portal, navigate to your App Service
- Click Advanced Tools → Go (opens Kudu)
- Click Tools → Zip Push Deploy
- Drag and drop the downloaded ZIP file into the deployment area
- Wait for deployment to complete (watch the logs)
- Restart your App Service if needed
- Database migrations will run automatically on next startup
Note: This method preserves your existing configuration and database. The ZIP contains only application files.
Method 2: Deploy to Azure Button (New installations only)
For fresh installations on an empty resource group:
- Go to the releases repository
- Click the Deploy to Azure button
- Select an empty resource group or create a new one
- Configure deployment parameters
Important: The Deploy to Azure button will fail on resource groups containing existing resources. Use Method 1 for existing deployments.
License Management¶
The portal requires a valid PowerStacks license to operate. License status is displayed in the Admin Dashboard and affects portal functionality.
Viewing License Status¶
- Go to Admin > Settings tab
- The License section shows:
- Current license status (Valid, Expired, Over Device Limit, etc.)
- License type and expiration date
- Device count vs. licensed limit
- Last validation timestamp
License Validation¶
The portal automatically validates your license: - On application startup - Every 24 hours - When you manually click Validate License
To force a validation check, click the Validate License button in the License section.
Updating License Key¶
- Go to Admin > Settings tab
- In the License section, enter your new license key
- Click Save License Key
- The portal validates the new key and displays the result
Alternatively, use the Setup Wizard to enter or update your license key.
License Warnings¶
Users see warning banners in the following situations:
| Condition | Banner Message |
|---|---|
| License expiring soon (≤30 days) | "License expires in X days. Please contact your IT administrator to renew." |
| Device count in grace period (up to 3% over limit) | "Device count exceeds license limit by X devices. Please contact your IT administrator to upgrade." |
| License invalid/expired | Warning message explaining the issue |
Note: When device count exceeds the license limit by more than 3%, new app requests are blocked until the license is upgraded or device count is reduced.
Device Count¶
The portal tracks managed devices from Intune that have checked in within the last 30 days. Device count is updated: - During each app sync from Intune - When you click Update Device Count in the License section
Display Settings¶
Configure the portal's visual appearance for all users.
| Setting | Description |
|---|---|
| Enable dark mode | Toggle dark mode on/off for all portal users (default setting) |
| Max featured apps on home page | Maximum number of featured apps to display in the home page carousel (default: 8) |
| Hero App | Select one app to feature prominently at the top of the home page |
Dark Mode Behavior:
The portal supports multiple dark mode sources with the following priority: 1. User preference - Users can click the sun/moon icon (☀️/🌙) in the header to toggle dark mode for themselves 2. System preference - If the user hasn't set a preference, the portal auto-detects the operating system's dark mode setting 3. Admin default - Falls back to the admin-configured dark mode setting
User preferences are stored in localStorage and persist across sessions. Users can always override the admin setting for their own viewing preference.
Dark Mode Styling: When dark mode is enabled, the portal uses a vignette-style design inspired by Microsoft Learn and Intune admin center: - Main content area: Darkest (#1a1a1a) with subtle inset shadow for depth - Header/Footer: Medium dark (#252525) with subtle borders - Outer edges: Lighter dark gray (#2d2d2d)
This creates a professional look where the center content draws focus while the periphery provides visual framing.
Note: Dark mode settings persist across page refreshes and login/logout cycles. The admin setting is loaded via a public API endpoint so it applies even before the user authenticates.
App Deployment Settings¶
| Setting | Description |
|---|---|
| Group Name Prefix | Prefix used when auto-creating Entra ID groups (default: AppStore-). Groups are named {prefix}{AppName}-Required. Use this to identify portal-managed groups in your tenant. |
Custom Domain Configuration¶
The Settings tab includes a Custom Domain section for configuring a custom domain (e.g., apps.yourdomain.com) for your portal.
Prerequisites¶
Before configuring a custom domain: 1. Your DNS must be configured with the appropriate CNAME or A record pointing to your Azure App Service 2. Your Azure App Service must be on the Basic tier or higher (required for custom domains with SSL)
Configuring via Admin Dashboard¶
- Go to Admin > Settings tab
- Scroll to the Custom Domain section
- Read the prerequisites and ensure DNS is configured
- Click Configure Custom Domain in Azure
- This opens the Azure Portal with a pre-configured ARM template that:
- Adds your custom domain to the App Service
- Creates a free Azure-managed SSL certificate
- Binds the certificate to your domain
After Configuration¶
Once your custom domain is configured:
- Update Entra ID Redirect URIs - Add your custom domain URLs to your App Registration
- Update Portal URL - In Communications > Email Notifications, update the Portal URL to use your custom domain
- Test Authentication - Sign out and sign back in to verify authentication works
Note: For detailed DNS configuration, certificate options, and troubleshooting, see CUSTOM-DOMAINS.md.
Communications¶
The Communications tab (formerly "Terms of Service") consolidates all notification, messaging, and company branding settings in one place.
Company Information¶
Configure your organization's branding details. These fields are used in notification messages and will support further message customization in future releases.
| Setting | Description |
|---|---|
| Company Name | Your organization's name, displayed in notifications |
| Company Logo | Upload a logo image (PNG/JPEG). Displayed in branded communications |
| Support Email | Contact email for support inquiries |
| Support Phone | Contact phone number for support |
Email Notifications¶
Configure how the portal sends email notifications for request submissions and approvals.
| Setting | Description |
|---|---|
| Enable email notifications | Toggle to turn email notifications on or off |
| Send As User ID | The Entra ID Object ID of the user or shared mailbox that will send emails. Find this in Azure Portal > Entra ID > Users > [select user] > Object ID |
| From Address | The email address displayed in the From field (should match the mailbox) |
| Portal URL | The URL of your portal, used in email links to direct users back to the portal |
Email Events — When email notifications are enabled, you can toggle individual events:
| Event | Description |
|---|---|
| Request Submitted | Notify requestor when their request is submitted |
| Approval Required | Notify approvers when their approval is needed |
| Request Approved | Notify requestor when their request is approved |
| Request Rejected | Notify requestor when their request is rejected |
| App Installed | Notify requestor when their app is installed on their device |
| App Published | Notify admin when a WinGet app is published to Intune |
Note: The app registration must have the
Mail.SendMicrosoft Graph application permission with admin consent granted.
Creating a Service Account for Email Notifications¶
For production deployments, we recommend creating a dedicated shared mailbox or service account with minimal permissions rather than using a personal user mailbox. This ensures email delivery is not tied to any individual's account.
Option A: Shared Mailbox (Recommended — No License Required)
- In the Microsoft 365 Admin Center, go to Teams & Groups > Shared mailboxes
- Click Add a shared mailbox:
- Name:
App Store for Intune(or your preferred name) - Email:
apprequests@yourdomain.com - Click Create
- Get the Object ID: Go to Azure Portal > Entra ID > Users > search for the shared mailbox > copy the Object ID
- In the portal admin settings, set:
- Send As User ID: The Object ID from step 4
- From Address:
apprequests@yourdomain.com
Shared mailboxes do not require a Microsoft 365 license and cannot be used for interactive sign-in, making them ideal for automated email sending.
Option B: Dedicated Service Account (License Required)
- In Azure Portal > Entra ID > Users > New user:
- Display name:
App Store for Intune Service - User principal name:
svc-apprequest@yourdomain.com - Assign a Microsoft 365 license with Exchange Online
- Disable interactive sign-in: Entra ID > Users > [service account] > Properties > Account enabled = No (or use Conditional Access to block interactive sign-in)
- Copy the Object ID and configure as with Option A
Permissions Required:
The portal sends emails using the Microsoft Graph Mail.Send application permission via the backend app registration. This permission allows the app to send mail as any user in the organization. To limit which mailbox the portal actually uses:
- Configure the Send As User ID in the portal settings to the specific shared mailbox or service account Object ID
- Optionally, use an Exchange Online Application Access Policy to restrict the
Mail.Sendpermission to only the designated mailbox:
# Connect to Exchange Online
Connect-ExchangeOnline
# Create a mail-enabled security group for allowed senders
New-DistributionGroup -Name "App Store Email Senders" -Type Security
# Add the shared mailbox to the group
Add-DistributionGroupMember -Identity "App Store Email Senders" -Member "apprequests@yourdomain.com"
# Restrict the app registration to only send from mailboxes in this group
New-ApplicationAccessPolicy `
-AppId "<your-api-client-id>" `
-PolicyScopeGroupId "App Store Email Senders" `
-AccessRight RestrictAccess `
-Description "Restrict App Store for Intune to send emails only from the designated mailbox"
# Test the policy (may take up to 30 minutes to propagate)
Test-ApplicationAccessPolicy -AppId "<your-api-client-id>" -Identity "apprequests@yourdomain.com"
Actionable Email Messages (Approve/Reject Buttons)¶
When enabled, approval notification emails include Approve and Reject buttons directly in the email body (Outlook Actionable Messages). Approvers can approve or reject requests without leaving their inbox.
Important: Actionable email buttons require a one-time provider registration with Microsoft and configuring the Originator / Provider ID in the portal settings. Without registration, emails will still be sent — they will contain the standard HTML body with a "Review Request" link to the portal. The Approve/Reject buttons will only appear once registration is complete and the Originator ID is configured.
| Setting | Description |
|---|---|
| Enable actionable email messages | Toggle to enable Approve/Reject buttons in approval emails |
| API Base URL for Email Actions | The base URL of your API (e.g., https://apprequest-prod-xxx.azurewebsites.net). Used for the button callback endpoints. |
| Originator / Provider ID | The Provider ID from your Microsoft Actionable Email registration. Required for Outlook to render action buttons. |
How it works:
1. When a request requires approval, the email includes an embedded MessageCard with Approve/Reject buttons
2. The MessageCard is embedded in the email <head> as application/ld+json — Outlook reads this to render action buttons
3. When an approver clicks Approve or Reject, Outlook sends an HTTP POST directly to your API
4. The API validates the request using a secure action token and processes the approval
5. Fallback behavior: If Outlook doesn't support Actionable Messages, or if the provider is not registered, or if the Originator ID is not configured, the email falls back to the standard HTML body with a "Review Request" link to the portal. Emails are always sent regardless of registration status — only the action buttons are affected.
Registering with Microsoft (Required for Action Buttons):
Outlook Actionable Messages require a one-time provider registration with Microsoft. Without this registration, Outlook will silently ignore the action buttons and only show the HTML fallback with a "Review Request" link.
- Go to the Actionable Email Developer Dashboard
- Sign in with your Microsoft 365 admin account
- Click New Provider and fill in:
- Friendly Name: App Store for Intune (or your preferred name)
- Sender email address: The From Address configured in your email settings (e.g.,
apprequests@company.com) - Target URL: Your API Base URL (e.g.,
https://apprequest-prod-xxx.azurewebsites.net) - Scope: Select Organization (your tenant only — auto-approved by tenant admin)
- Public Key: Leave blank (not required for organization-scoped registrations)
- Submit the registration — organization-scoped registrations are auto-approved by the tenant admin
- Copy the Provider ID (GUID) from your registration
- In the portal admin settings, paste the Provider ID into the Originator / Provider ID field
- Allow up to 24 hours for the registration to take effect
Verifying Exchange Online Settings:
If action buttons still don't appear after registration, verify that Actionable Messages are enabled in Exchange Online:
# Check organization-level settings (both should be True)
Get-OrganizationConfig | FL ConnectorsActionableMessagesEnabled, SmtpActionableMessagesEnabled
# If disabled, enable them
Set-OrganizationConfig -ConnectorsActionableMessagesEnabled $true -SmtpActionableMessagesEnabled $true
# Check per-mailbox setting (should be True)
Get-Mailbox -Identity apprequests@company.com | FL ConnectorsEnabled
Important: Microsoft is transitioning Actionable Messages from External Access Tokens (EAT) to Entra ID token authentication. If Microsoft requires Entra ID tokens for new registrations, the portal's action endpoints may need to be updated in a future release. See the Entra ID Token documentation for details.
Microsoft Teams Bot Notifications¶
Send personal Teams notifications to approvers and requestors via a Teams Bot. Each user receives individual Adaptive Card messages in their Teams chat. This uses Microsoft Bot Framework proactive messaging.
| Setting | Description |
|---|---|
| Enable Teams bot notifications | Toggle to turn Teams bot notifications on or off |
| Bot App ID | Your API Client ID (the bot reuses the API app registration) |
| Test | Send a test notification to yourself to verify the bot is working |
| Approval Required | Notify approvers when their approval is needed |
| Request Approved | Notify requestor when their request is approved |
| Request Rejected | Notify requestor when their request is rejected |
| App Installed | Notify requestor when their app is installed on their device |
| App Published | Notify admin when a WinGet app is published to Intune |
Prerequisites¶
- An Azure Bot resource registered in Azure Portal using your API app registration's Client ID (see SETUP.md)
- The Microsoft Teams channel enabled on the Azure Bot resource
- The bot pre-installed for users via Teams Admin Center setup policies
No separate
Bot__environment variables are needed — the bot usesAzureAd__ClientId,AzureAd__ClientSecret, andAzureAd__TenantIddirectly.
Configuring the Portal¶
- Go to Admin > Communications tab
- Scroll to Microsoft Teams Bot Notifications
- Enable Enable Teams bot notifications
- Enter the Bot App ID (your API Client ID — the bot reuses the same app registration)
- Click Test to send a test notification to yourself
- Select which events should trigger notifications
- Click Save Settings
How It Works¶
- The bot is pre-installed for users via Teams Admin Center setup policies
- When the bot is installed for a user, Teams sends a
conversationUpdateevent — the portal stores a conversation reference for that user - To send a notification, the portal retrieves the stored conversation reference and uses Bot Framework proactive messaging
- For pooled approvals (group-based), the portal expands the group membership and sends individual messages to each group member
- For sequential approvals, only the current stage approvers are notified
- Notifications are sent as Adaptive Cards with request details and action buttons
Troubleshooting¶
- Bot not sending messages: Verify the bot is installed for the user by checking the
BotConversationReferencestable - Test notification fails: Ensure the bot is installed for your user account first
- 401 errors: Verify the Azure Bot resource's Microsoft App ID matches your
AzureAd__ClientIdand the client secret is valid - Some users don't receive notifications: The Teams Admin Center setup policy may take up to 24 hours to propagate
Note: No additional Microsoft Graph API permissions are required for Teams bot notifications. Bot Framework handles its own authentication.
Approval Reminders¶
Automatically send reminder emails for pending approvals.
| Setting | Description |
|---|---|
| Enable approval reminders | Toggle to enable/disable automatic reminders |
| Reminder interval (days) | Days before the first reminder is sent (default: 2) |
| Max reminders | Maximum number of reminders per request (default: 3) |
Stale Request Escalation¶
Automatically escalate requests that have been pending too long.
| Setting | Description |
|---|---|
| Enable escalation | Toggle to enable/disable automatic escalation |
| Escalation threshold (hours) | Hours before a request is escalated (default: 48) |
| Recipient email(s) | Comma-separated email addresses for escalation notifications |
| Recipient group | Entra ID group whose members receive escalation notifications |
Terms of Service¶
The Terms of Service section remains within the Communications tab, allowing admins to create and manage TOS versions that users must accept.
App Management¶
The App Management tab is your central hub for managing which Intune apps are available in the portal and how they behave.
Syncing Apps from Intune¶
- Click the Sync Apps from Intune button
- The portal fetches all mobile apps from your Intune tenant
- Apps are imported with their name, publisher, description, icon, and category
- New apps are hidden by default - you must make them visible for users to see them
Incremental Sync: After the first full sync, subsequent syncs are incremental — only new and modified apps are fetched from Intune. The portal tracks the last sync date and uses lastModifiedDateTime filtering to minimize Graph API calls. Icons are fetched in parallel (5 concurrent requests) for faster sync times.
Scheduled Background Sync: A background service automatically syncs apps on a schedule: - Nightly full sync at 2:00 AM UTC — catches deletions and ensures consistency - Hourly incremental sync — picks up new and changed apps quickly - The manual Sync Apps from Intune button is still available for on-demand syncs
Pagination: The portal follows @odata.nextLink pagination from the Graph API, so tenants with more than 999 apps are fully supported.
Filtering the App List¶
The filter bar above the app table has six axes (v1.26.0+). All filters compose with the search box and persist while you navigate the tab:
| Filter | Values | Default |
|---|---|---|
| Platform | All / Windows / iOS / Android / macOS / Web (only platforms present in your tenant) | All |
| Visibility | All / Visible / Hidden | All |
| Category | All / (per-tenant categories) | All |
| Source | All / Store / Intune | All |
| Purpose | Install / Update / All | Install |
| Search | Free-text match against app name and publisher | empty |
Source distinguishes apps published through the App Store catalog flow (where the portal owns the lifecycle) from apps that already existed in Intune and were synced into the portal. Source = Store shows only apps with sourceWingetPackageId set — these are the apps where Version & Rollback, ring-based update deployments, and the Publish wizard apply.
Purpose distinguishes the user-facing Install Win32 app from the paired Update Win32 app that drives the two-app update model. The default is Install so the table shows the user-facing apps you'd expect; flip to Update when you need to inspect Update apps directly (for example to verify an Update app's group assignments or ring activation state). The flag is set automatically during Intune sync by joining against UpdateDeployment.UpdateIntuneAppId — there is no manual classification step.
App Table Columns¶
The app list table is read-only and informational. Click any row to open the app's detail view where you can edit settings.
| Column | Description |
|---|---|
| App | App name, icon, and publisher |
| Platform | App platform badge (Windows, iOS, Android, macOS, Web) |
| Type | App type from Intune (e.g., Win32, Microsoft Store, iOS Store) |
| Assigned | Status badge showing whether a target group is configured (Yes/No/N-A) |
| Visible | Status badge showing whether users can see this app in the portal (Yes/No/N-A) |
| Approval | Status badge showing whether approval is required (Yes/No/N-A) |
| Date Added | When the app was first synced or published |
Supported App Types¶
The portal supports self-service deployment for specific app types. When syncing apps from Intune, each app is classified by platform and support status:
| Platform | Supported Types | Unsupported Types |
|---|---|---|
| Windows | Win32, Microsoft Store (New) | Windows Universal (LoB), MSI (LoB), AppX (LoB) |
| iOS | iOS Store, iOS VPP | iOS (LoB) |
| Android | Android Store, Managed Google Play, Android for Work | Android (LoB) |
| Web | Web Apps | - |
| macOS | - | All macOS app types |
Why some types are unsupported: - Line of Business (LoB) apps require special handling during deployment that the portal cannot automate - macOS apps have different deployment mechanisms that are not yet implemented
Unsupported apps appear in the admin UI with their type displayed and an "N/A" status badge. Admins can see these apps exist but cannot make them available for self-service.
Configuring Individual Apps¶
Click any app row in the table to open its detail view. The detail view has two sections accessible from the left navigation:
- Overview — Quick summary cards showing platform, visibility, approval status, assignment type, date added, and version
- Properties — Grouped property sections (described below), each with an Edit link
Editing App Settings¶
There are three ways to edit app settings from the detail view:
- Side panel (quick edit) — Click "Edit" on the Visibility or Approval section to open a slide-out panel for fast single-field changes
- Settings wizard — Click "Edit" on the Assignment or Deployment Options section to open the full 5-step wizard, starting at the relevant step
- Edit All Settings — Click the "Edit All Settings" button to open the wizard from step 1
The App Settings Wizard has 5 steps:
| Step | Title | Fields |
|---|---|---|
| 1 | Visibility & Store | Visible, Featured, Category, Cost |
| 2 | Approval | Requires Approval, Acknowledgment |
| 3 | Assignment | Assignment type, Target group, Assignment filter |
| 4 | Deployment Options | Install behavior, Restart behavior, Notifications, Grace period (Win32 only) |
| 5 | Review + Save | Summary of all changes with diff highlighting |
Visibility Settings¶
- Yes: App appears in the user-facing app catalog
- No: App is hidden from users but still tracked in the system
- N/A: App type is not supported for self-service deployment
Use this to control which Intune apps are available for self-service requests. Apps that shouldn't be requested (system apps, dependencies, etc.) should remain hidden.
Hiding Apps with Active Deployments:
When you set an app's visibility to No and it has an active Intune deployment and Azure AD group, a confirmation dialog appears asking whether you also want to delete the deployment group and assignment:
- OK: Hides the app AND removes the Intune assignment and deletes the Azure AD deployment group
- Cancel: Only hides the app but preserves the deployment infrastructure (useful if you plan to make it visible again later)
Note: Deleting the deployment and group is permanent. Users who currently have the app will lose access when group membership is removed.
Automatic Deployment Setup:
When you set an app's visibility to Yes for the first time, the portal automatically:
- Creates an Entra ID Security Group named
{GroupNamePrefix}{AppName}-{arch}-{locale}-v{version}-Required - Example:
AppStore-Microsoft Teams-x64-en-US-v1-0-0-Required - The prefix is configurable in Settings (default:
AppStore-) -
Dots in version numbers are replaced with dashes (e.g.,
1.0.0becomesv1-0-0) -
Creates an Intune App Assignment
- The app is assigned as Required to the new security group
- Assignment type (User or Device) is based on the app's Assignment setting
This automation ensures that when a user's request is approved, they simply need to be added to the group and Intune handles the deployment.
Note: If group or assignment creation fails, the error is logged but the visibility change still succeeds. You can manually create the assignment in Intune if needed.
Approval Settings¶
- Yes: Requests go through the configured approval workflow before completion
- No: Requests are auto-approved and the user/device is immediately added to the target group
- N/A: App type is not supported
Approval can only be enabled for visible apps. If you try to enable approval for a hidden app, you'll see a warning: "App must be visible in the store to enable approval."
Delete from Intune¶
From the app detail view, apps with a real Intune deployment (not synced apps with a winget- prefix) can be deleted. This removes the app from both Intune and the portal entirely.
Confirmation flow:
- Step 1 — "Are you sure you want to delete [app name] from Intune?"
- Click Delete to proceed, or Cancel to abort
- Step 2 (only for portal-published apps with a deployment group) — "This app has a deployment group (group name). Do you also want to delete the deployment group?"
- Click Yes to delete the group, or No to keep it for reuse when republishing
What gets deleted:
| Action | Always | Only if "Yes" to group deletion |
|---|---|---|
| Win32 app removed from Intune | Yes | — |
| Intune assignment removed | Yes | — |
| All associated requests removed | Yes | — |
| App record removed from portal | Yes | — |
| Entra ID security group deleted | — | Yes |
After deletion, the app is completely removed from the portal. To restore it, republish from the WinGet catalog.
When to use Delete from Intune: - You need to republish an app with updated settings (e.g., different silent switches, updated installer) - An app deployment is in a bad state and needs to be recreated - You want to permanently remove an app from both Intune and the portal
Edit App Modal¶
Click Edit on any app row to open the Edit App Modal, which provides access to all configurable app properties:
App Details Section¶
| Field | Description |
|---|---|
| Cost | Optional cost to display on app cards (informational only, no billing). Enter a decimal value or leave empty for "Free". |
| Category | App category displayed on app cards. Start typing to see suggestions from existing categories in your catalog. You can enter any category name. |
Assignment Settings Section¶
| Field | Description |
|---|---|
| Assignment Type | User (add requester to group) or Device (add requester's device to group) |
| Target Group | Entra ID security group for app assignment. Click "Search Groups" to browse, or use "Clear" to remove. |
| Assignment Filter | Optional Intune assignment filter. Select filter type (Include/Exclude) and choose a filter. |
Win32 Deployment Options Section¶
These options appear only for Win32 apps and control Intune deployment behavior:
| Field | Description |
|---|---|
| Install Context | System (machine-wide) or User (per-user) installation |
| Device Restart | How to handle restart after install: Based on Return Code, Allow, Suppress, or Force |
| End User Notification | What users see: Show All, Show Reboot Only, or Hide All |
| Allow Available Uninstall | Whether users can uninstall from Company Portal |
| Restart Grace Period | Enable/configure grace period before forced restart |
Click Save to apply changes or Cancel to discard.
App Icon¶
Each app displays an icon in the catalog. Icons are typically synced from Intune, but some apps may be missing icons. The Edit App modal provides two ways to set an icon:
- Upload Icon — Upload a PNG or JPEG image file from your computer
- Browse Library — Opens the Icon Library picker, which shows all unique icons already used by other apps in the portal. Search by app name or publisher, then click an icon to apply it instantly. This is useful when multiple apps share the same publisher icon or when you want to reuse an existing icon without re-uploading.
Assignment Type¶
Determines what gets added to the Entra ID group when a request is approved:
- User: The requesting user is added to the group (most common)
- Device: The user's device is added to the group (useful for device-targeted deployments)
For Device assignment: - The portal auto-detects the user's current device based on browser/OS information - Devices matching the detected OS are pre-selected in the dropdown - The user can override the selection if they want to install on a different device - If a primary device is set in Intune and matches the current OS, it takes priority - The selected device is added to the target AAD group - Intune deploys the app to that specific device
Device Detection Logic: 1. Browser detects the operating system (Windows, macOS, iOS, Android) 2. Portal matches against the user's registered Intune devices 3. Matching devices are marked as "(Detected)" in the dropdown 4. A helpful message confirms the auto-detection: "This device was automatically detected based on your current browser."
Configuring Approval Workflows¶
Click the Workflow button on any app to open the Approval Workflow Editor.
No Approval Required¶
For apps with "Approval" set to "No", requests are auto-approved immediately. No workflow configuration is needed.
Simple Approval¶
For apps that just need someone from the Approver Group to sign off: 1. Set "Approval" to "Yes" 2. Leave the workflow with no stages 3. Any member of the configured Approver Group can approve
Manager + Additional Approvers¶
- Enable Require Manager Approval
- Choose workflow type:
- Linear: Specific users approve in sequence
- Pooled: Any member of specified groups can approve
- Add approval stages as needed
Conditional Stages (v1.8.2)¶
Each approval stage can be made conditional, so it only applies when specific criteria are met:
- Open the Approval Workflow Editor for an app
- Add a stage and assign an approver or group
- Toggle "Make this stage conditional"
- Add conditions using the condition builder:
- Select a condition type (Department, Cost Center, Job Title, Location, Platform, Request Count)
- Choose an operator (Equals, Not Equals, Contains, etc.)
- Enter the comparison value
- Add multiple conditions with AND/OR logic
- A human-readable summary of the conditions is shown on the stage card
See APPROVAL-WORKFLOWS.md for detailed condition types and operators.
See APPROVAL-WORKFLOWS.md for detailed workflow configuration options.
Version History and Rollback¶
The portal tracks version history for apps published from the App Catalog, enabling you to rollback to previous versions if a new version introduces issues.
Viewing Version History¶
- Go to Admin > App Management tab
- Find the app in the app table
- Open the app's detail view by clicking the row
- The Version History modal displays all recorded versions
Understanding Version Status¶
Each version in the history shows a status badge:
| Status | Description |
|---|---|
| Current (blue) | The version currently deployed in Intune |
| Archived (gray) | Previous version that was deployed, available for rollback |
| Failed (red) | Deployment or publish attempt that failed |
Version Information Displayed¶
For each version, you'll see:
- Version number - The semantic version (e.g., "1.2.3")
- Recorded date - When this version was detected/published
- Updated by - User who published this version (if available)
- Installer size - Size of the installer package
- Release notes - Change summary for this version
- Package ID - WinGet package identifier (e.g., "7zip.7zip")
Rolling Back to a Previous Version¶
If a new app version causes issues, you can rollback to a previous version:
- Open the Version History modal for the app
- Find the version you want to rollback to (must have "Archived" status)
- Click the Rollback button on that version
- Click Confirm Rollback to proceed
- The portal will:
- Re-publish the selected version to Intune
- Update the app assignment with the older version
- Mark the rolled-back version as "Current"
- Archive the previously current version
Important: Rollback is only available for versions marked as "Archived". Failed deployments and the current version cannot be rolled back to.
Version History Limitations¶
- Winget apps only: Version history is only tracked for apps published from the App Catalog
- Manual Intune apps: Apps added directly to Intune (not via portal) do not have version history
- Retention policy: By default, the portal keeps all versions indefinitely. Admins can configure retention limits in Settings
Troubleshooting Rollback Issues¶
Rollback button is grayed out: - Only "Archived" versions can be rolled back to - Ensure the version's installer is still available
Rollback fails:
- Check Graph API permissions (DeviceManagementApps.ReadWrite.All)
- Verify the installer URL is still accessible
- Review API logs for detailed error messages
Version history is empty: - Version history only tracks apps published through the portal - If you synced an existing Intune app, its pre-sync versions are not tracked - Only new publishes/updates will appear in history
Pending Approvals¶
The Pending Approvals tab shows all requests waiting for your approval.
Viewing Requests¶
Each request shows: - Requestor: Name and email of the person requesting the app - App: The app being requested - Requested: When the request was submitted - Stage: Current approval stage number - Justification: Reason provided by the requestor (if any)
Approving Requests¶
- Review the request details
- Click Approve to advance the request
- The request moves to the next approval stage (or completes if this was the final stage)
Rejecting Requests¶
- Click Reject
- Enter a reason for rejection (required)
- The requestor is notified of the rejection with your reason
User Experience¶
Browsing Apps¶
The portal provides a Microsoft Store-style browsing experience:
Home Page: - Hero section featuring a prominently displayed app - Featured apps carousel with navigation controls - Category sections showing apps grouped by category - Quick links to Browse Apps and My Requests - Platform badges (Windows, iOS, Android, macOS, Web) on app cards - "New" badges on apps added within the last 14 days
Browse Apps Page: - Search apps by name, publisher, or description - Filter apps by category using the dropdown - Filter apps by platform (Windows, iOS, Android, etc.) - Featured apps section at the top - Apps organized by category with platform badges
Visual Indicators: - Platform badges - Show the app's target platform with icons (🪟 Windows, 🍎 iOS, 🤖 Android, 🍏 macOS, 🌐 Web) - "New" badge - Green badge on apps added within the last 14 days - "Featured" badge - Gold badge on featured apps - Price indicator - Shows cost or "Free" label
App Detail Page: When users click on any app card, they see a detailed view including: - Large hero banner with app icon and blurred background - App name, publisher, and category badges - "Featured" badge if applicable - Get button to request the app - Price or "Free" indicator - Full description - App information (Publisher, Version, Category, Platform, Approval status)
How Users Request Apps¶
Users can request apps in two ways:
Quick Request (Get button): 1. Click the Get button on any app card (Home, Browse Apps, or App Detail page) 2. The request modal opens directly 3. If Device assignment, select the target device 4. Enter optional justification 5. Click Submit Request
From App Detail Page: 1. Click on an app card to open the App Detail page 2. Review the app description and information 3. Click the Get button 4. Complete the request form and submit
Request Status Flow¶
| Status | Description |
|---|---|
| Pending | Request is awaiting approval |
| Approved | All approvals complete, processing assignment |
| Rejected | Request was rejected by an approver |
| Processing | System is adding user/device to AAD group |
| Completed | User/device successfully added to group |
| Failed | Error occurred during processing |
My Requests¶
Users can view their request history by clicking My Requests in the navigation. This shows all requests they've submitted with current status.
Request New App¶
Users can request apps that aren't in the catalog by clicking the + Request New App button on the Browse Apps page.
How It Works¶
- User fills out the form with app name, publisher, description, and optional download URL
- The portal sends an email notification to all members of the Admin Group
- The email includes:
- Requestor name and email
- App name and publisher
- Business justification provided by the user
- Download URL (if provided)
- Suggestions for how to add the app (Winget catalog or manual upload)
- The request is logged in the audit trail
Admin Actions¶
When you receive a new app request email:
- Evaluate the request: Is this app appropriate for your organization?
- Find the app:
- Check the App Catalog in Admin Dashboard for easy publishing
- Search for the app in Intune if it's already available
- Download from the vendor if needed
- Add to portal:
- Use App Catalog to publish directly to Intune, or
- Manually add the app to Intune and sync
- Configure visibility: Make the app visible in the portal
- Notify the user: Reply to the email or notify the user directly
Configuration¶
The Request New App feature uses your existing email notification settings:
- Admin Group: Members receive the notification emails
- Email Settings: Uses the same
Mail.Sendconfiguration as other notifications
No additional configuration is required. If email notifications are disabled, the feature will return an error to the user.
Reports & Analytics¶
The Admin Dashboard includes comprehensive reporting capabilities to help you understand app request patterns and deployment status.
Accessing Reports¶
- Navigate to Admin in the navigation menu
- The dashboard displays summary tiles at the top
- Use the tabs to navigate between report views:
- Summary - Overview statistics with install status
- Trends - Visual charts showing request patterns over time
- By Person - Detailed request history by user
- Install Status - Deployment status for approved requests
Summary Dashboard¶
The Summary view shows key metrics as clickable tiles:
| Tile | Description |
|---|---|
| Total Requests | Total number of app requests in the system |
| Pending | Requests awaiting approval |
| Approved | Requests that have been approved |
| Rejected | Requests that were rejected |
| Pending Install | Approved requests waiting for Intune deployment |
| Installing | Apps currently being installed on devices |
| Installed | Successfully deployed apps |
| Install Failed | Apps that failed to install |
The install status tiles (Pending Install, Installing, Installed, Install Failed) are color-coded for quick identification: - Pending Install (blue) - Deployment pending - Installing (orange) - Installation in progress - Installed (green) - Successfully deployed - Install Failed (red) - Deployment failed
Trends Tab¶
The Trends tab provides visual analytics to help identify patterns and popular applications.
Request Trends Chart¶
The main trends chart shows: - Requested (blue line) - Number of new requests per day - Completed (green line) - Number of completed requests per day - Visual area fills under each line for easy comparison
Use the time range dropdown to view: - Last 7 days - Last 14 days - Last 30 days - Last 90 days
Top Requested Apps¶
A horizontal bar chart showing the most frequently requested applications, helping you identify: - Popular apps that might benefit from auto-approval - Apps that may need better visibility or promotion - Patterns in user requests
Status Distribution¶
A breakdown showing the distribution of request statuses: - Total count and percentage for each status - Visual progress bars for comparison - Separate sections for request status and install status
Install Status Tracking¶
The portal automatically tracks the deployment status of approved requests for Intune apps.
How It Works¶
- Initial Status: When a request is approved for an Intune-managed app, the install status is set to "Pending Install"
- Background Polling: A background service checks Intune for deployment status every 15 minutes
- Status Updates: The portal updates the install status based on Intune's reported deployment state
- Final Status: Once installed (or failed), the status stops being polled
Install Status Values¶
| Status | Description |
|---|---|
| Not Applicable | App is not tracked for install status (e.g., Winget apps) |
| Pending Install | Request approved, waiting for Intune to begin deployment |
| Installing | Intune is actively installing the app on the device |
| Installed | App successfully installed and detected on the device |
| Install Failed | Installation failed - check the error message for details |
| Uninstalled | App was installed but has since been removed |
Viewing Install Status¶
Admin Dashboard: 1. Go to Admin > Reports section 2. View install status counts in the summary tiles 3. Click on any status tile to filter by that status 4. Use the Install Status tab for detailed view
Install Status Tab: The dedicated Install Status tab shows: - Summary counts for each install state - List of all requests with their current install status - Last checked timestamp for each request - Error messages for failed installations
Troubleshooting Install Status¶
Status stuck on "Pending Install": - Verify the device is online and connected to Intune - Check that the user/device is correctly added to the target AAD group - Review Intune device sync status in the Intune admin center
Status shows "Install Failed": - Check the error message in the Install Status column - Common issues: - Disk space insufficient - App dependencies not met - User cancelled the installation - Device compliance issues blocking deployment
Status not updating:
- The polling service runs every 15 minutes
- Check API logs for any errors in the InstallStatusPollingService
- Verify the app registration has DeviceManagementApps.Read.All permission
By Person Report¶
The By Person tab allows you to search for a specific user and view all their app requests:
- Enter a user's name or email in the search box
- View their complete request history
- See status, install status, and timestamps for each request
- Use the Retry button on failed requests to re-attempt group membership
Audit Trail¶
The Audit Trail tab provides a comprehensive log of all portal activity for compliance and security monitoring.
Accessing the Audit Trail¶
- Go to Admin > Reports section
- Click the Audit Trail button in the report navigation
- Use filters to narrow down results
Available Filters¶
| Filter | Description |
|---|---|
| Search | Free-text search across user email, action, entity, and details |
| Action Type | Filter by specific action (e.g., Request.Submitted, App.Suggested) |
| Entity Type | Filter by entity type (e.g., Request, App, Settings) |
| Start Date | Show events from this date onwards |
| End Date | Show events up to this date |
Audit Log Information¶
Each audit entry includes:
| Field | Description |
|---|---|
| Timestamp | When the action occurred |
| User | Email address of the user who performed the action |
| Action | The type of action performed |
| Entity Type | The type of object affected |
| Entity ID | Identifier of the affected object |
| Details | Additional context (varies by action type) |
| IP Address | The IP address of the user |
Common Actions Logged¶
| Action | Description |
|---|---|
Request.Submitted |
User submitted an app request |
Request.Approved |
Approver approved a request |
Request.Rejected |
Approver rejected a request |
Request.Completed |
Request was fulfilled (user added to group) |
App.Suggested |
User submitted a new app request via Request New App form |
Settings.Updated |
Admin changed portal settings |
Apps.Synced |
Admin synced apps from Intune |
Exporting Audit Logs¶
- Apply any desired filters
- Click the Export CSV button
- A CSV file downloads with all matching audit entries
- Use this for compliance reporting or external analysis
Audit Log Retention¶
Audit logs are stored indefinitely in the SQL database. There is no automatic purge. For organizations with high volume, consider:
- Periodic export to long-term storage
- Database scaling if performance is affected
- Custom retention policies via direct database management
Best Practices¶
App Visibility Strategy¶
- Sync all apps to get your full Intune catalog
- Keep system apps hidden (dependencies, frameworks, required apps)
- Make user-requestable apps visible (productivity tools, optional software)
- Use categories from Intune to help users find apps
Approval Configuration Strategy¶
| App Type | Recommended Setting |
|---|---|
| Free productivity tools | No approval required |
| Licensed software | Manager approval |
| Admin/privileged tools | Multi-stage with IT Security |
| Developer tools | Manager + IT approval |
Group Management¶
With automatic group and assignment creation, the portal handles most group management for you:
- Automatic Setup: When you make an app visible, the portal creates a security group and Intune assignment automatically
- Consistent Naming: Groups follow the pattern
{GroupNamePrefix}{AppName}-{arch}-{locale}-v{version}-Required(e.g.,AppStore-Microsoft Teams-x64-en-US-v1-0-0-Required). Dots in version numbers are replaced with dashes. - Custom Prefix: Configure the Group Name Prefix in Settings to match your organization's naming conventions
- Manual Override: You can still manually set a Target Group on an app if you prefer to use an existing group
Tip: To use existing groups instead of auto-created ones, set the Target Group before making the app visible.
Email Notifications¶
- Create a dedicated shared mailbox for notifications
- Grant the app
Mail.Sendpermission on that mailbox - Use a recognizable From address like
apprequest@company.com - Include your portal URL so users can click through to view status
App Catalog & Cloud Packaging¶
The App Catalog tab in the Admin Dashboard allows you to browse over 9,000 apps from Microsoft's official WinGet repository (microsoft/winget-pkgs) and publish them directly to Intune as Win32 apps. Package manifests are fetched directly from GitHub (no third-party services), ensuring zero supply chain attack risk.
Browsing the App Catalog¶
- Navigate to Admin > App Catalog tab
- Popular packages are displayed 24 per page with pagination controls
- Use the search box to find specific apps (search uses "Load More" infinite scroll)
- Each package shows: name, publisher, version, and description
- Icons are loaded automatically where available
Publishing to Intune¶
- Find the app you want to publish
- Select architecture and locale (if available):
- Each package card displays available architectures (e.g., x64, x86, arm64) and locales (e.g., en-US, de-DE, fr-FR)
- If multiple options are available, they appear as dropdown selectors
- If only one option is available, it appears as a label showing what will be published
- Default selections: x64 for architecture, en-US for locale
- Click Publish to Intune button on the package card
- A packaging job is created with your selected architecture and locale
- Monitor job status in the Packaging Jobs section below the catalog
Architecture, Locale, and Version Tracking:
When apps are published from the WinGet catalog:
- The selected architecture, locale, and version are stored in the packaging job
- When the app is synced from Intune, it automatically inherits these values
- Deployment groups are named to include all three: AppStore-AppName-x64-en-US-v25-10-186-0-Required
- Dots in version numbers are replaced with dashes (e.g., 25.10.186.0 becomes v25-10-186-0)
- This helps identify which variant and version of multi-architecture apps is deployed, and prevents duplicate deployments of the same version
Packaging Jobs¶
The Packaging Jobs section shows all queued and completed packaging operations:
| Status | Description |
|---|---|
| Pending | Job is queued, waiting for processing |
| Downloading | Downloading the installer from WinGet manifest URL |
| Packaging | Wrapping in PSADT v4 and creating .intunewin package |
| Uploading | Uploading package to Intune via Graph API |
| Creating | Creating Win32LobApp in Intune |
| Completed | App successfully created in Intune |
| Failed | Error occurred - click Retry to requeue |
Packaging Architecture¶
The packaging process runs in-process on the App Service — no separate containers, agents, or functions needed:
- Queue Message: When you click "Publish to Intune", a job is added to Azure Storage Queue
- Background Service: The in-process
PackagingQueueServicepicks up the job - Download: Installer is downloaded from the URL in the WinGet manifest
- Wrapping (PSADT mode only): Installer is wrapped in PSAppDeployToolkit v4 for standardized deployment
- Package Creation:
.intunewinpackage is created using the cross-platform SvRooij.ContentPrep library - Intune Upload: The package is uploaded and a Win32LobApp is created via Graph API
Packaging Methods: Raw vs PSADT¶
When publishing an app from the App Catalog, you can choose between two packaging methods using the dropdown on each package card:
| Method | Description |
|---|---|
| Raw (default) | Packages the installer directly. The installer executable is the entry point — Intune runs it with silent switches. Simpler, smaller package size. |
| PSADT | Wraps the installer in PSAppDeployToolkit v4. Provides standardized logging, pre/post-install hooks, and consistent exit codes across all installer types. Larger package size due to framework files. |
When to use PSADT: - You need standardized deployment logging across all apps - You want consistent install/uninstall behavior regardless of installer type - You need the pre-installation and post-installation hooks (e.g., closing running applications before install)
When to use Raw: - You want the simplest, most direct installation - Package size matters (PSADT adds ~5 MB of framework files) - The app's native installer already handles silent installation well
Raw Packaging Details¶
In Raw mode, the installer is packaged directly into the .intunewin file. The install and uninstall commands sent to Intune depend on the installer type:
Install commands by file type:
| File Type | Install Command | Uninstall Command |
|---|---|---|
.msi |
msiexec /i "installer.msi" {silent switch} |
msiexec /x "installer.msi" {silent switch} |
.exe |
"installer.exe" {silent switch} |
Registry lookup (see below) |
.msix / .appx |
powershell.exe Add-AppxPackage -Path 'installer.msix' |
Registry lookup (see below) |
Registry-based uninstall (for non-MSI): For EXE and other non-MSI installers, the uninstall command searches the Windows registry (HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall and the Wow6432Node equivalent) for an entry matching the app's display name. It uses the QuietUninstallString if available, otherwise appends /S /silent /quiet to the UninstallString.
PSADT Packaging Details¶
In PSADT mode, the installer is placed inside a PSAppDeployToolkit v4 folder structure. The PSADT framework provides a standardized wrapper around the installer with logging, error handling, and consistent exit codes.
Package structure created:
Package/
+-- Invoke-AppDeployToolkit.exe <-- Entry point (what Intune runs)
+-- Deploy-Application.ps1 <-- Generated install/uninstall logic
+-- AppDeployToolkit/ <-- Framework files (from PSADT v4 template)
| +-- AppDeployToolkitMain.ps1
| +-- AppDeployToolkitHelpers.ps1
| +-- ...
+-- Files/
+-- installer.exe (or .msi) <-- Your actual installer
Intune commands (all PSADT packages use the same commands):
- Install: Invoke-AppDeployToolkit.exe -DeploymentType Install -DeployMode Silent
- Uninstall: Invoke-AppDeployToolkit.exe -DeploymentType Uninstall -DeployMode Silent
Generated Deploy-Application.ps1 script variables:
| Variable | Value |
|---|---|
$appVendor |
Publisher name from the Winget manifest |
$appName |
Package name from the Winget manifest |
$appVersion |
(empty — version tracked via Intune metadata) |
$appLang |
EN |
$appRevision |
01 |
$appScriptVersion |
1.0.0 |
$appScriptAuthor |
AppRequestPortal |
Install section — what runs during installation:
For MSI installers:
PSADT automatically adds /qn /norestart when running in Silent deploy mode. If a custom silent switch was provided that includes MSI properties (e.g., TRANSFORMS=..., PROPERTY=VALUE), those are passed via -AddParameters.
For EXE installers:
The -WaitForMsiExec parameter ensures PSADT waits if another MSI installation is already in progress. EXE installers always get explicit silent switches since PSADT doesn't know the correct flags for each installer.
Uninstall section — what runs during uninstallation:
For MSI installers:
For non-MSI installers, the script searches the registry for the app's uninstall string:
$regPaths = @(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$app = Get-ItemProperty $regPaths |
Where-Object { $_.DisplayName -like '*AppName*' } |
Select-Object -First 1
if ($app.QuietUninstallString) {
Execute-Process -Path 'cmd.exe' -Parameters "/c $($app.QuietUninstallString)"
} elseif ($app.UninstallString) {
Execute-Process -Path 'cmd.exe' -Parameters "/c $($app.UninstallString) /S /silent /quiet"
}
Repair section: Runs the same commands as the install section. PSADT supports Invoke-AppDeployToolkit.exe -DeploymentType Repair -DeployMode Silent for reinstallation.
Error handling: If the PSADT script encounters a fatal error, it exits with an error code in the 60000 range and logs the error details. PSADT's built-in logging writes to C:\Windows\Logs\Software\ on the target device.
The following PSADT error codes are mapped as "Failed" return codes in Intune:
| Exit Code | Description |
|---|---|
60001 |
General PSADT error (catch block in Deploy-Application.ps1) |
60002 |
Error during pre-installation phase |
60003 |
Error during installation phase |
60008 |
Generic non-zero exit from the wrapped installer |
These are registered in the Intune Win32 app return code mappings so that PSADT failures are properly reported in Intune and reflected in the portal's install status tracking.
PSADT fallback: If PSADT wrapping fails for any reason (e.g., template download failure, extraction error), the system automatically falls back to Raw packaging. The job will still complete successfully — just without the PSADT wrapper.
PSADT template caching: The PSADT v4 template is downloaded once from the official GitHub releases and cached in Azure Blob Storage (psadt-template/PSAppDeployToolkit_Template_v4.zip). Subsequent packaging jobs reuse the cached template.
Silent Switches¶
Silent switches tell the installer to run without displaying any UI. The portal resolves the silent switch in this order:
- Winget manifest value — if the manifest specifies
InstallerSwitches.Silent, that value is used - Installer type default — based on the
InstallerTypefield in the Winget manifest - File extension fallback — based on the installer's file extension
- Last resort —
/S /silent
Default silent switches by installer type:
| Installer Type | Silent Switch | Used By |
|---|---|---|
| MSI / WiX | /qn /norestart |
Windows Installer packages |
| InnoSetup | /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- |
Inno Setup installers |
| Nullsoft / NSIS | /S |
NSIS installers |
| Burn | /quiet /norestart |
WiX Burn bootstrapper bundles |
| EXE (generic) | /S /silent |
Generic executables |
These defaults are applied in both Raw and PSADT modes.
Detection Scripts¶
Every app published to Intune includes an auto-generated PowerShell detection script. Intune runs this script on target devices to determine whether the app is already installed.
Detection strategy (in order of preference):
-
ProductCode detection (most reliable) — If the Winget manifest includes
AppsAndFeaturesEntrieswith aProductCode(MSI GUID), the script checks for that exact GUID in the registry uninstall paths. This is the most precise detection method. -
DisplayName detection (fallback) — If no ProductCode is available, the script searches the registry for an entry where
DisplayNamematches the app name (using wildcard matching). Publisher name is also checked if available in the manifest.
Version checking: When a version number is available from the Winget manifest (via AppsAndFeaturesEntries.DisplayVersion or the package version), the detection script compares the installed version against the required minimum version. Apps with older versions are reported as "Not Installed" so Intune will upgrade them.
Registry paths checked:
- HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
- HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
Script execution context: Detection scripts run as 64-bit PowerShell (runAs32Bit: false) without signature enforcement (enforceSignatureCheck: false). The script exits with code 0 if the app is detected, or 1 if not installed.
Intune Win32 App Configuration¶
When the app is created in Intune via the Graph API, the following settings are applied:
| Setting | Value |
|---|---|
| Applicable architectures | x64, x86 |
| Minimum OS | Windows 10 1903 |
| Install context | System (runs as SYSTEM account) |
| Device restart behavior | Based on return code |
Return codes configured:
| Code | Type | Meaning |
|---|---|---|
0 |
Success | Installation completed successfully |
1707 |
Success | Installation completed successfully (MSI) |
3010 |
Soft reboot | Installation succeeded, reboot recommended |
1641 |
Hard reboot | Installation succeeded, reboot initiated |
1618 |
Retry | Another installation is in progress, retry later |
App icon: If available, the app icon is fetched automatically (via Google S2 Favicon API or GitHub publisher avatar) and included as a PNG or JPEG in the Intune app metadata.
Setting Up Packaging¶
Packaging works out of the box with the default deployment. No additional setup is required beyond:
- Azure Storage Account (deployed by default via Bicep template)
- Graph API permissions for Intune app management (see Prerequisites)
- Verify connectivity:
- Go to Admin > App Catalog
- Try publishing a test app (e.g., "7-Zip")
- Check Packaging Jobs for status
The PSADT v4 template is automatically downloaded from the official GitHub repository on first use and cached in Azure Blob Storage.
Manifest Integrity Verification¶
By default, the packaging pipeline trusts WinGet manifests fetched from raw.githubusercontent.com. Each manifest declares a SHA256 for its installer, and the pipeline verifies the downloaded installer matches before publishing to Intune. That defends against a tampered installer at the publisher's download server, but it does not defend against a tampered manifest on the GitHub fetch path.
To close that gap, enable Settings → "Verify manifest integrity against Microsoft's signed index".
When the setting is enabled:
- Before packaging any WinGet-sourced app, the portal downloads Microsoft's signed community index (
source2.msix, ~10 MB) fromhttps://cdn.winget.microsoft.com/cache/source2.msix. - The portal extracts the SQLite database embedded in that MSIX and looks up the package by its identifier.
- If the package is not present in the index, the packaging job fails immediately with an explicit "Manifest integrity check failed" error. There is no silent fallback.
- The downloaded MSIX and extracted SQLite are cached for 12 hours to avoid re-downloading the index on every packaging job.
When to enable this: Any environment that wants the same manifest-trust posture as the WinGet client itself. The cost is a one-time ~10 MB download every 12 hours and a brief extra step on the first packaging job after a cache refresh.
When to leave it off: If you've already implemented your own integrity controls (e.g. a hardened internal mirror of the WinGet repo with its own signing pipeline) and prefer to rely on those.
Failure mode: If verification fails for a specific package, only that packaging job fails — other jobs continue normally. To temporarily bypass verification (for example, to package a package that is genuinely missing from the index due to an upstream removal), an admin can disable the setting from the Settings page and retry. Re-enable it afterward.
For the full security chain and v2 roadmap (per-version manifest hash comparison, MSIX signature validation), see SECURITY.md → Package Verification.
App Updates¶
The App Updates tab in the Admin Dashboard shows all apps that have been published from WinGet and are being tracked for version updates.
Tracked Apps Table:
| Column | Description |
|---|---|
| App | App name and publisher |
| WinGet Package ID | The WinGet identifier (e.g., Microsoft.VisualStudioCode) |
| Published Version | Version currently in your portal/Intune |
| Latest Version | Latest version available in the WinGet repository |
| Status | Steady state for this app — Update Available, Up to Date, Unknown (sticky, when the last deploy attempt failed), or Check failed (sticky, when the most recent WinGet update lookup couldn't resolve a version). Unknown is intentionally non-alarming — when packaging or assignment fails, we genuinely don't know what state Intune is in until you investigate via the activity bell or the Packaging Jobs tab. Transient deploy progress (Queued, Packaging, Assigning, etc.) is surfaced in the activity bell at top-right of the admin pages, not in this column. |
| Ring Template | Name of the ring template configured for this app, or "—" when the app uses custom rings or ringless mode (per-app settings live in the Update Ring Configuration side panel) |
| Last Checked | When the version was last compared. A small red dot is shown when the most recent check failed (e.g., the WinGet package ID could not be resolved) |
| Actions | Deploy Update or Dismiss (only shown when update is available). Deploy Update opens a confirmation wizard showing exactly what will be deployed (app, version, ring mode, target, ring breakdown, auto-deploy setting) before you commit. |
Select any row in the table to open a per-app detail view where you can configure ring deployment, set the auto-deploy override, and run an immediate update check (Check Now) just for that app — useful when you want to confirm a single app's status without waiting for the next scheduled run.
Automatic Tracking:
- Apps published from the WinGet catalog are automatically tracked for updates
- The SourceWingetPackageId is backfilled automatically by the background service (no manual Intune sync required)
- Update checks run on a configurable schedule (default: every hour when enabled)
- The "Last Checked" date now stamps on every attempt — success or failure — so a stale date in the UI tells you "this app's lookup has been failing", not "the service is broken". The failure reason is shown on the per-app detail view under Last Check Result.
Actions:
- Check for Updates: Manually trigger an update check for all tracked apps
- Check Now (per-app, on the detail view): Run an update check for a single app without waiting for the schedule
- Deploy Update: Opens a confirmation wizard (v1.24.0+) showing the deployment summary — app, version transition, ring mode (template/custom rings/ringless), ring breakdown or ringless target+filter, and the per-app auto-deploy override. Once you select Deploy in the wizard, a packaging job is queued that downloads the new version from WinGet, wraps it in PSADT, archives the .intunewin to blob storage for rollback, creates a new Install Win32 app in Intune (advancing the portal's version pointer) and a paired Update Win32 app with a requirement script that targets only devices already running the app. Watch the activity bell at top-right for in-flight progress.
- Dismiss: Hides the update notification for that app
Activity Bell (v1.24.0+):
The bell icon at the top-right of every admin page is an Intune-style task tracker. The badge shows the count of in-flight deploys plus any sticky-failed entries; click the bell to open a drawer listing recent activity with each entry's current state, age, and any error or pause message. Adaptive polling: 15 seconds while at least one task is in-flight, 60 seconds otherwise to keep the badge accurate; stops entirely when nothing's happening and the drawer is closed (so you're not burning API calls just to show a zero badge). Use this when you need to know what's happening right now across all your deploys; use the App Updates Status column when you need to know what state each app is in; use the Activity tab (below) when you need the full historical timeline.
Dismissing sticky failures (v1.27.0+):
Each terminal entry in the bell drawer (Up-to-Date, Packaging Failed, Assignment Failed) has a × dismiss button. Dismissing removes the entry from the drawer's default view and from the Activity Log tab's default view. Active entries — Queued, Packaging, Assigning, Paused — can't be dismissed; they're still moving and would just reappear on the next poll. A new failure for the same app is a distinct row, so a re-failure shows up regardless of whether you dismissed the prior one. Dismissals persist per-browser via localStorage. To restore everything you've dismissed, open the Activity tab and click Restore dismissed.
Activity tab (v1.27.0+):
Full unified timeline of packaging jobs and update deployments — the same data the bell drawer surfaces, but with no terminal-entry cap. Use this when you need to answer "what happened last week" or "did this app's last three deploys all fail?" — questions the 8-entry drawer can't span.
The filter bar above the activity table:
- State — All / Active (in flight) / Failed / Up-to-Date / Paused
- Time — Last 24 hours / 7 days / 30 days / 90 days / All time (default 30 days)
- Search — free-text match against app name and version
- Show dismissed — toggle to include entries you've dismissed in the bell drawer
Filters compose. The table paginates at 25 rows per page so a long history doesn't bog the page down. Each row has its own Dismiss button mirroring the bell drawer's behaviour, plus the standard state badge, started/finished timestamps, and the deploy's error or pause message.
The Run cleanup button on this tab runs both retention sweeps in one click:
- Packaging jobs older than 30 days (keeping the 100 most-recent)
- Update deployments older than 90 days (keeping the 100 most-recent)
In-flight and Paused work is never deleted regardless of age, and the most-recent N rows are always preserved regardless of age. Cleanup is manual — there is no scheduled background sweep, so retention happens on whatever cadence admins choose.
The bell drawer's footer link View all activity → deep-links straight here (/admin?tab=activity).
Recovery for stuck or orphaned deploys (v1.24.0+):
If a deploy gets stuck (e.g., App Service restart killed the worker mid-packaging) or completes packaging but the Update app handler doesn't advance the deployment, the recovery action is reachable from either side of the same orphaned state:
- Packaging Jobs table surfaces Cancel and Cleanup for in-flight stuck jobs and Cleanup orphaned deploy for completed jobs whose linked deployment is still on Packaging.
- Update Deployments table (v1.26.0+) surfaces a parallel Cancel and Cleanup for deployments stuck on Packaging.
Both routes call the same cancel-and-cleanup endpoint: one click marks the packaging job Failed for audit, marks the linked deployment Failed, and deletes the per-job blob folder to free storage. The Cleanup actions are time-gated to deployments that have been stuck for more than 10 minutes — brand-new deployments legitimately sit at Packaging for a few minutes while the Update-app handler runs, so the button is hidden until the threshold is exceeded. Re-trigger the deploy from App Updates → Deploy Update when ready — the new attempt starts clean.
Update Ring Templates¶
Ring templates define a staged deployment strategy for app updates. Instead of deploying an update to all devices at once, rings let you roll out to a pilot group first, wait a configurable number of days, then expand to broader groups.
Managing Templates:
In Admin > Settings, scroll to the Update Ring Templates section:
- Click Create Template to define a new ring template
- Each template has a name and one or more rings (up to 10)
- Each ring has a name (e.g., "IT Pilot"), a delay in days, and one or more Entra ID security groups
- Each ring has its own deployment settings: available/deadline timing, install behavior, restart settings, notifications, and delivery optimization
- Click Show Deployment Settings on any ring to configure available time (days + time of day), deadline time, restart grace period, snooze settings, and more
- Ring 1 typically has a 0-day delay (deploys immediately); subsequent rings deploy after their configured delay
- Set one template as the default to auto-assign it to new apps
- Click Import from Autopatch to discover Windows Autopatch ring groups from your Entra ID and pre-populate rings with those groups
Example Configuration:
| Ring | Name | Delay | Groups |
|---|---|---|---|
| 1 | IT Pilot | 0 days | IT-Pilot-Devices |
| 2 | Early Adopters | 3 days | Early-Adopters |
| 3 | Production | 7 days | All-Managed-Devices |
This means Ring 1 deploys immediately, Ring 2 deploys 3 days later, and Ring 3 deploys 7 days after Ring 2 — a total rollout of 10 days.
Per-App Configuration:
For apps published from the Winget catalog, select the app's row on the App Updates tab to open its detail view. The detail view includes an Update Rings section with three deployment styles:
- Ringless — updates deploy immediately to a single Intune assignment with no staged rollout. Pick All Devices or All Users as the target (default: All Devices), and optionally apply an Intune assignment filter to narrow the audience (e.g. limit by OS version or department). Ringless still creates the same two-app Install/Update pair as ringed mode (so rollback, install/update telemetry, and the lifecycle Status column work identically) — it just collapses the rollout to a single ring targeting the chosen built-in audience.
- Use a template — apply a reusable ring template defined under Update Ring Templates. If a default template is set, it is preselected.
- Custom rings — define rings specific to this one app. Same per-ring controls as a template (groups, availability/deadline timing, install behavior, restart settings, end-user notifications, delivery optimization, assignment filter), scoped to a single app. Use this when no global template fits — for example when this app needs a different pilot group, a different rollout pace, or a specific assignment filter.
When you select Custom rings, choose Edit custom rings to open the ring editor. Edits in the editor are held in the side panel until you select Save — switching between deployment styles before saving doesn't lose your in-progress custom rings. Ringless target/filter choices are likewise preserved when you flip to a ringed mode and back, so admins can experiment without losing prior settings.
The detail view also has an Auto-Deploy Updates section to set the per-app override (use the global default, always auto-deploy, or never auto-deploy). As of v1.23.0 auto-deploy works for ringless apps too — the only safety check is that the Update Ring Configuration side panel has been opened at least once for the app (so the admin has explicitly picked ringed or ringless mode rather than leaving it unconfigured).
Note (v1.20.0): The Update Rings and Auto-Deploy controls used to live on the App Management tab's app detail view. They have moved to the App Updates tab because not every app under App Management is tracked for updates — only apps published from the WinGet catalog are.
Ring-Based Deployments¶
When you click Deploy Update for an app with ring-based updates enabled, the system:
- Creates a packaging job for the new version (same as non-ringed updates)
- When packaging completes, creates a new "Update" Win32 app in Intune
- Activates Ring 1 immediately — creates Required assignments for Ring 1's groups
- Schedules subsequent rings based on their configured delays
- A background service checks every 15 minutes for rings that are ready to activate
- When each ring's delay expires, its groups receive the Required assignment
Deployment Dashboard:
The App Updates tab shows active and recent deployments below the updates table:
- Ring progress indicator — colored circles show which ring is active
- Click a deployment row to expand and see detailed ring status, schedule dates, and group assignments
- Pause — stops ring progression (active ring stays deployed, next ring is held)
- Resume — restarts ring progression after a pause
- Advance — manually skip the delay for the next ring (useful for accelerating after pilot validation)
- Halt rollout — removes active ring assignments and marks the deployment rolled back. Devices that already updated keep the new version; future rings are stopped. (Renamed from Rollback in v1.26.0 to disambiguate from the App-level rollback that swaps the Install app back to the prior version on all devices.)
- Cancel — removes all assignments and marks the deployment as cancelled
- Cancel and Cleanup — for deployments stuck on Packaging for more than 10 minutes. Marks the deployment Failed, marks the linked packaging job Failed, and deletes the blob folder for that job in one atomic action. Mirrors the Cancel and Cleanup button on the Packaging Jobs table.
Two-App Model:
When you deploy an update — ringed or ringless (v1.23.0+) — the system creates two separate Win32 apps in Intune:
- Install app (user-targeted) -- immediately updated to the new version so new installs always get the latest. Existing assignments are preserved.
- Update app -- a separate Win32 app with a requirement script that only installs on devices that already have the app. Devices without the app see it as "Not Applicable." Assignments are created on this app:
- In ringed mode, one Required assignment per group per ring, activated as the rollout progresses.
- In ringless mode, a single Required assignment to All Devices or All Users (the admin's choice in the Update Ring Configuration side panel), with an optional Intune assignment filter. Created and activated immediately at deploy time — there's only one ring to roll out.
This separation ensures: - New device enrollments always get the latest version (no vulnerability window) - Updates to existing devices are controlled separately from new installs (and, in ringed mode, gated through rings with health checks) - User-targeted and device-targeted assignments don't conflict
Per-Ring Assignment Settings:
When a ring activates, the per-ring deployment settings configured on the template are applied to the Intune assignment. Each ring's assignment includes:
- Available time -- computed from the ring activation time plus the ring's "Available after days" + "Available at time" (or "as soon as possible" if not configured)
- Deadline time -- computed the same way for the install deadline (or none if not configured)
- Time zone -- assignment uses device local time when "Use device time zone" is enabled, otherwise UTC
- End user notifications -- Show all, Show reboot only, or Hide all
- Delivery optimization priority -- Foreground or Not configured
- Restart settings -- grace period, countdown, and snooze duration (omitted when restart behavior is "Suppress")
- Assignment filter -- optional Intune filter (Include or Exclude) scoping the ring to a subset of devices
Settings are read from the deployment-time snapshot on the ring, so changes to the template after a deployment starts do not affect rings already in flight.
Per-Ring Assignment Filters:
Each ring in a template can target an Intune assignment filter. Open the ring's "Show Deployment Settings" panel and pick a filter from the list (Windows-platform filters from your tenant). Choose Include matching devices or Exclude matching devices, or clear the filter to assign without one. The filter is snapshotted onto the deployment ring at deploy time alongside the other ring settings.
Install App Rollback:
If a new version has installation issues, you can roll back the Install app to the previous version with one click. In the app detail view, the Version & Rollback section card (top of the Properties view, v1.26.0+) has a primary Roll back to previous version button. This swaps the Install app back to the previous version's Intune app (which still exists in Intune), so new installs receive the older working version. No re-packaging is needed.
To pick a specific historical version (rather than the immediate predecessor), select View full history… in the same section card. The version history modal lists every published version with archive status and a per-version Rollback button.
Update App Rollback:
Rolling back a ring-based deployment removes the Intune assignments from active rings so no additional devices receive the update. Devices that already installed the update retain the new version -- Intune does not uninstall it.
Signal-Based Ring Progression¶
Ring advancement is not just delay-based -- the system checks device install health from Intune before advancing to the next ring. This prevents a failed update from rolling out broadly.
How it works:
After a ring's delay expires, the progression service queries Intune for device install status before advancing:
| Condition | Action |
|---|---|
| Success rate >= threshold (default 95%) | Advance to next ring |
| Failure rate > failure threshold (default 10%) | Auto-pause deployment, notify admin |
| Not enough data yet (evaluation period not met) | Wait, even if delay expired |
Configuration (on ring templates):
| Setting | Default | Description |
|---|---|---|
| Minimum success rate | 95% | Required success rate before advancing |
| Maximum failure rate | 10% | Failure rate that triggers auto-pause |
| Minimum evaluation period | 24 hours | Wait time after ring activation before checking status |
Set the minimum success rate to 0% to advance based on delay only (disables health checks).
Auto-Deploy Updates¶
Updates can be deployed automatically when detected, eliminating the need to click "Deploy Update" for each app.
Global setting (Admin > Settings): - Auto-deploy updates -- when enabled, all apps with ring-based updates configured will automatically package and deploy through their rings when a new version is detected
Per-app override (App detail view): - Always auto-deploy -- this app always auto-deploys, regardless of the global setting - Never auto-deploy -- this app never auto-deploys, regardless of the global setting - Use global default -- follows the global setting
Safety requirements: - Auto-deploy only works for apps with ring-based updates enabled. Apps without rings configured will still require manual "Deploy Update" clicks, even if auto-deploy is on. This ensures all auto-deployed updates go through staged rings. - The "Deploy Update" button on the Updates tab always works for manual one-off deployments regardless of auto-deploy settings.
Winget Integration Settings¶
In Admin > Settings > Winget Integration:
| Setting | Description |
|---|---|
| WinGet Repository URL | GitHub repository URL for WinGet packages. Default: https://github.com/microsoft/winget-pkgs (Microsoft's official repository). Format: https://github.com/owner/repo or owner/repo. Organizations with custom/private WinGet repositories can point to their internal GitLab/GitHub mirror. Warning: Changing this will clear the entire package cache. |
| GitHub Personal Access Token | Recommended. Required for the "Show More Results" live search feature (GitHub's Code Search API requires authentication). Also increases API rate limits from 60/hour to 5,000/hour for faster cache syncs. Create a classic token at https://github.com/settings/tokens with public_repo scope. |
Why Use a GitHub Token?¶
Without token (unauthenticated):
- 60 API requests per hour
- Initial cache sync takes 2-3 hours
- May hit rate limits during heavy use
- "Show More Results" live search will not work — GitHub's Code Search API requires authentication. Only cached results and exact package ID lookups (e.g., Google.Chrome) will return results.
With token (authenticated): - 5,000 API requests per hour - Initial cache sync takes 30-60 minutes - Reliable operation even with many users - Full live search — "Show More Results" queries the entire WinGet repository in real time via GitHub Code Search, finding packages that may not yet be in the local cache
Creating a GitHub Personal Access Token:
- Go to GitHub Settings → Personal Access Tokens → Tokens (classic)
- Click "Generate new token" → "Generate new token (classic)"
- Give it a descriptive name: "App Store for Intune WinGet Integration"
- Select scope: public_repo (Access public repositories)
- Click "Generate token"
- Copy the token (starts with
ghp_...) - Paste into Admin Settings → WinGet Integration → GitHub Personal Access Token
- Click Save Settings
Note: Tokens are stored securely in Azure Key Vault and never exposed in logs or UI.
Local Development Testing¶
For developers testing the packaging feature locally:
- Prerequisites:
- Azure Storage account configured (uses the same storage queue and blob container as production)
-
Graph API credentials configured for Intune access
-
Configure the API (
src/AppRequestPortal.API/appsettings.json): -
Ensure
AzureStorage:ConnectionStringpoints to your Azure Storage account -
Run the services:
-
Test: Go to Admin > App Catalog, search for an app, click "Publish to Intune". The in-process background service will pick up the job and process it.
Troubleshooting Packaging¶
Jobs stuck in Pending:
- Verify the App Service is running and the background service started (check Application Insights logs for PackagingQueueService)
- Ensure the AzureStorage:ConnectionString is configured correctly
- Check that the storage queue packaging-jobs exists
Jobs failing during download: - The service downloads directly from the installer URL in the WinGet manifest - Check if the package exists in the Microsoft winget-pkgs repository - Review logs for HTTP errors or GitHub API rate limits
Jobs failing during PSADT wrapping:
- PSADT wrapping has graceful fallback — if it fails, the raw installer is packaged instead
- Check logs for PsadtService entries
- First-time use requires downloading the PSADT template from GitHub; ensure outbound HTTPS is allowed
Jobs failing during upload:
- Verify the API has Graph API permissions for Intune (DeviceManagementApps.ReadWrite.All)
- Review API logs for Graph API errors (detailed error messages are logged)
Database Maintenance¶
The App Store for Intune uses Azure SQL Database Basic tier, which includes comprehensive automatic maintenance features. No manual database maintenance is required.
Why No Manual Maintenance is Needed¶
Azure SQL Database handles all maintenance tasks automatically, including:
| Feature | Description |
|---|---|
| Automatic Index Tuning | Azure monitors query patterns and automatically creates, drops, or rebuilds indexes to optimize performance |
| Automatic Plan Correction | Detects and fixes query plan regression issues automatically |
| Automatic Backups | Point-in-Time Restore (PITR) with 7-day retention (Basic tier), up to 35 days on higher tiers |
| Geo-Redundant Storage | Backups are stored redundantly across Azure regions for disaster recovery |
| Automatic Updates | Database engine patches and security updates applied automatically with no downtime |
| Automatic Statistics | Query statistics are automatically updated to ensure optimal query plans |
What About Maintenance Scripts?¶
You may be familiar with on-premises SQL Server maintenance solutions like Ola Hallengren's Maintenance Solution that schedule index rebuilds, integrity checks, and backup jobs. These are not needed for Azure SQL Database because:
-
Index Maintenance: Azure's automatic tuning handles index optimization. For a Basic tier database under 2GB, index fragmentation has minimal impact on performance.
-
Integrity Checks (DBCC CHECKDB): Azure runs these automatically. You cannot schedule them yourself on Azure SQL Database.
-
Backup Jobs: Azure manages all backups automatically. You cannot create your own backup jobs—instead, you use Azure's built-in Point-in-Time Restore feature.
-
Statistics Updates: Azure automatically updates statistics as needed. Manual
UPDATE STATISTICScommands are rarely necessary.
Backup and Recovery¶
Azure SQL Database provides built-in backup and recovery capabilities:
| Tier | PITR Retention | Backup Storage |
|---|---|---|
| Basic | 7 days | Geo-redundant (GRS) |
| Standard | 35 days | Geo-redundant (GRS) |
| Premium | 35 days | Geo-redundant (GRS) |
To restore your database:
- Go to Azure Portal > SQL databases > your database
- Click Restore in the toolbar
- Select a restore point (any time within retention period)
- Azure creates a new database with data as of that point in time
Note: Restoring creates a new database—it does not overwrite the existing one. You would rename databases after verifying the restore if needed.
When to Consider Scaling Up¶
The Basic tier (2GB, 5 DTUs) is suitable for the App Store for Intune's typical workload. Consider scaling up if you observe:
- Consistent high DTU usage (>80%) in Azure metrics
- Slow query response times
- Timeouts during peak usage
To scale up: 1. Go to Azure Portal > SQL databases > your database 2. Click Compute + storage 3. Select a higher tier (Standard, Premium) or increase DTUs 4. Changes take effect within minutes with minimal downtime
Manual Maintenance (If Ever Needed)¶
In rare cases where you need to manually optimize, you can:
-- View index fragmentation (informational only)
SELECT
OBJECT_NAME(ips.object_id) AS TableName,
i.name AS IndexName,
ips.avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 30
ORDER BY ips.avg_fragmentation_in_percent DESC;
-- Rebuild a specific index if needed (usually not necessary)
ALTER INDEX [IX_AppRequests_UserId] ON [AppRequests] REBUILD;
However, for the App Store for Intune's typical data volumes (hundreds to thousands of app requests), this manual intervention is almost never necessary.
Disaster Recovery & Backups¶
The App Store for Intune includes built-in disaster recovery features to protect your data.
What's Automatically Protected¶
| Component | Protection | Recovery |
|---|---|---|
| SQL Database | Automated backups (7-day PITR) + geo-redundant storage | Restore to any point in time |
| Storage Account | Geo-redundant (GRS) with 6 copies across 2 regions | Automatic failover available |
| Key Vault Secrets | Soft delete (7-day recovery) | Recover deleted secrets |
| Application Code | GitHub repository + immutable release packages | Redeploy from ARM template |
Quick Recovery Actions¶
Restore deleted data (SQL):
az sql db restore --resource-group <rg> --server <server> \
--name AppRequestPortal --dest-name AppRequestPortal-Restored \
--time "2026-02-14T10:00:00Z"
Recover deleted Key Vault secret:
Rollback to previous app version:
az webapp config appsettings set --resource-group <rg> --name <app> \
--settings WEBSITE_RUN_FROM_PACKAGE="https://github.com/powerstacks-corp/app-store-for-intune/releases/download/v1.5.5/AppRequestPortal.zip"
High Availability (Optional)¶
For organizations requiring higher uptime, see the Disaster Recovery Guide for: - SQL Active Geo-Replication setup - Traffic Manager / Azure Front Door configuration - Multi-region deployment patterns
Monthly Backup Verification¶
We recommend testing your recovery capability monthly: 1. Restore SQL database to a point 24 hours ago (test environment) 2. Verify data integrity 3. Delete test database 4. Document actual recovery time
Troubleshooting¶
Apps Not Syncing¶
- Verify the app registration has
DeviceManagementApps.Read.Allpermission - Check that admin consent is granted
- Look at API logs for Graph API errors
Users Not Seeing Apps¶
- Verify the app's Visible status is set to Yes (open the app detail view to check)
- Check that the user is authenticated
- Confirm the app has synced (check Last Sync Date)
Approvals Not Working¶
- Verify the approver is in the correct AAD group
- For Linear workflows, ensure the correct person is approving
- Check that the workflow is properly configured on the app
User Not Added to Group¶
- Verify the app's Target Group is configured
- Check the app registration has
Group.ReadWrite.Allpermission - Look at API logs for Graph API errors
Email Notifications Not Sending¶
- Verify
Mail.Sendpermission has admin consent - Check the Send As User ID is a valid Object ID
- Confirm Enable email notifications is toggled on in the Communications tab
- Look at API logs for email sending errors
Assignment Filter Dropdown Is Empty¶
When configuring per-ring filters in Update Ring Templates or per-app Custom rings, the filter dropdown should list every assignment filter that exists in your Intune tenant. If the dropdown is empty (or only shows "No filters available") even though filters do exist in Intune:
- Verify the API app registration has
DeviceManagementConfiguration.Read.All(Application) permission. This permission was missing from setup docs prior to v1.22.1 — older deployments often lack it. - Confirm admin consent has been granted for that permission.
- After adding the permission, refresh the page; results are not cached.
- If filters still don't appear, confirm filters actually exist in Intune (Intune admin center → Tenant administration → Filters).
Request Shows "Failed" Status¶
When a request shows as "Failed" after approval, it means the system couldn't add the user to the target group. Common causes:
- User already in group - Fixed in v1.2.0; now treated as success
- Group doesn't exist - The target group may have been deleted
- Permission issue - The app lacks
GroupMember.ReadWrite.Allpermission - Invalid group ID - The group ID configured for the app is incorrect
To retry a failed request: 1. Go to Admin > Reports > By Person 2. Search for the user 3. Find the failed request and click Retry
Viewing Azure App Service Logs¶
When troubleshooting issues, you can view detailed logs in Azure. There are three main options:
Option 1: Log Stream (Easiest - Real-time)¶
View logs as they happen. This is the quickest way to see what's happening:
- Go to the Azure Portal
- Navigate to your App Service (e.g.,
apprequest-prod-xxxxx) - In the left menu, under Monitoring, click Log stream
- Logs will appear in real-time as requests are made
Tip: Keep Log Stream open in one tab while reproducing the issue in another tab. Reproduce the error and watch the logs appear.
Option 2: Application Insights (Historical Queries)¶
View historical logs and run queries. This is useful for investigating past issues.
Setup Required: Application Insights requires a connection string and (optionally) API credentials to be configured in your Azure App Service. If you haven't set this up yet, see Step 12: Configure Application Insights in the Setup Guide. The key settings are: -
APPLICATIONINSIGHTS_CONNECTION_STRING— enables telemetry collection (logs, traces, exceptions) -ApplicationInsights__AppIdandApplicationInsights__ApiKey— enables the metrics dashboard in the Admin panelImportant: You must navigate to the Application Insights resource directly (e.g.,
ai-apprequest-prod), NOT the "Logs" blade inside App Service. The App Service > Logs blade queries Log Analytics tables (AppServiceConsoleLogs), not Application Insights tables (traces).
Steps:
1. Go to the Azure Portal
2. Go to your Resource Group
3. Click on the Application Insights resource (e.g., ai-apprequest-prod)
4. In the left menu, click Logs
5. Close the "Queries" popup if it appears
6. Use these queries:
View recent errors (severity 3+):
View all logs from the last hour:
Search for group operations:
traces
| where message contains "AddUserToGroup" or message contains "group"
| order by timestamp desc
| take 50
View exceptions:
If you see "No tables" or "Failed to resolve table 'traces'": - You may be in the wrong location. Make sure you're in Application Insights > Logs, not App Service > Logs - If Application Insights was just deployed, wait 5-10 minutes for data to appear - Ensure the portal is running version 1.2.0+ (which includes Application Insights integration)
Option 3: App Service Logs (Log Analytics)¶
If you're in the App Service > Logs blade, it uses different tables. Use these queries instead:
View console output:
View HTTP logs:
Check what tables are available:
Option 4: Enable Detailed Filesystem Logging¶
For even more detail, enable filesystem logging:
- Go to your App Service in Azure Portal
- Under Monitoring, click App Service logs
- Enable Application Logging (Filesystem) and set level to Information or Verbose
- Enable Detailed error messages
- Click Save
Logs are then available in Log Stream and can be downloaded from Advanced Tools (Kudu) > Debug Console > LogFiles