Appearance
This guide applies to imper.ai for Hiring, where interviews are scheduled in Microsoft 365 or Outlook.
To secure an interview, imper.ai needs Calendars.ReadWrite - to see the scheduled interview, and to attach the candidate's verification link to it. Granted through the standard consent flow, it applies to every mailbox in your tenant, and Microsoft offers no way to narrow it at consent time.
This guide shows how to grant that permission to a specific set of users instead, using a security group that you control. Once configured, the group's membership is the access list: adding an interviewer grants access, removing them revokes it, and no further changes to the configuration are needed.
NOTE
This is optional. If you are comfortable granting tenant-wide calendar access during the standard consent flow, no additional configuration is required and you can skip this guide.
This guide covers Calendars.ReadWrite only. CallRecords.Read.All and User.ReadBasic.All, which imper.ai uses to confirm who joined a Teams interview, are tenant-wide by nature and cannot be scoped to a group.
If the tenant-wide permission has already been consented to, revoke it under Microsoft Entra ID > Enterprise applications > imper.ai > Permissions before you begin. Microsoft treats the two grants as additive, so a tenant-wide grant left in place keeps access to every mailbox and the group restriction has no effect.
Who Should Use This
- Hiring teams where only recruiters and interviewers should be visible to imper.ai, not the whole organization
- Organizations with data-access policies that require application permissions to be scoped
- Pilots and phased rollouts, where only a subset of interviewers should be covered initially
What You Will Need
- An account with the Exchange Administrator role, or membership in the Organization Management role group
- PowerShell 7 or later, on Windows, macOS, or Linux
- One value supplied by your imper.ai contact: the imper.ai Application ID
Everything else this guide needs is looked up from your own tenant.
How It Works
You define which mailboxes imper.ai may access by creating a security group, then granting imper.ai calendar access limited to that group. The grant lives in Exchange rather than in Entra ID, and it names the group rather than the individual users - so the group's membership is the only thing you maintain afterwards.
Step 1 - Fill in your values
Every command in this guide reads from the variables below. Fill this block in once, paste it into your PowerShell session, and then run the remaining steps unchanged - there is nothing else to substitute by hand.
powershell
# ─── Your tenant ────────────────────────────────────────────────────────────
$organization_domain = "yourdomain.com"
# ─── The users imper.ai should be able to see ───────────────────────────────
# One line per interviewer or recruiter. You can change this list later
# without repeating any other step - see "Changing Who Is Covered".
$users_list = @(
"recruiter@$organization_domain"
"interviewer@$organization_domain"
)
# ─── The value supplied by your imper.ai contact ────────────────────────────
$app_id = "<imper.ai Application ID>"
# ─── Names for the objects this guide creates - the defaults are fine ───────
$group_name = "imper-calendar-access"
$group_email = "$group_name@$organization_domain"
$scope_name = "imper calendar scope"
$assignment_name = "imper calendar access"What this does: Defines the values used by every later command. Nothing is created or changed yet.
Where the user list comes from: if you already know the addresses of the recruiters and interviewers to cover, type them in. Otherwise export them from the system that schedules your interviews - in Workday, a report of the workers who act as interviewers, exported to CSV - and read the addresses straight from the file instead of pasting them in one by one:
powershell
$users_list = (Import-Csv .\interviewers.csv).EmailReplace Email with whichever column of the export holds the work email address.
Expected result: No output. Confirm the values were accepted:
powershell
$organization_domain, $group_email, $app_id
$users_listIMPORTANT
These variables live only in the current PowerShell session. If you close the window, or the session times out, paste this block again before continuing - otherwise later commands will run with empty values and create objects with the wrong names.
Step 2 - Connect to Exchange Online and Microsoft Graph
powershell
Install-Module ExchangeOnlineManagement -Scope CurrentUser
Install-Module Microsoft.Graph -Scope CurrentUser
Import-Module ExchangeOnlineManagement
Import-Module Microsoft.Graph.Applications
Connect-ExchangeOnline
Connect-MgGraph -Scopes "Application.Read.All"What this does: Installs and loads Microsoft's two management modules, then signs you in to each. Exchange Online owns the group, the access rule and the grant. Microsoft Graph is used once, in Step 3, to look up imper.ai's identity in your directory.
Expected result: A browser window opens for each sign-in. Use your Exchange Administrator account. The commands used in the following steps become available only after both sign-ins complete.
NOTE
The Graph connection is read-only - Application.Read.All allows looking up an application, not modifying one. If your organization requires admin consent for the Graph PowerShell module, an administrator grants it once at first sign-in.
Step 3 - Look up imper.ai's identity
powershell
$sp = Get-MgServicePrincipal -Filter "appId eq '$app_id'"
$sp.Id, $sp.DisplayNameWhat this does: Finds the imper.ai application in your tenant by its Application ID and stores it in $sp. The Id returned is the object ID Exchange needs in the final step. Doing it now means the whole guide runs from variables, with nothing to look up in a portal later.
Expected result: Two lines - a GUID, then imper.ai. Confirm the display name matches before continuing.
NOTE
An application has two different object IDs in Entra: one under Enterprise applications and a different one under App registrations. Only the first works here. Get-MgServicePrincipal returns that one, which is why this guide looks the value up rather than asking you to copy it from the portal.
Step 4 - Create the security group
powershell
New-DistributionGroup -Name $group_name `
-Alias $group_name `
-Type Security `
-PrimarySmtpAddress $group_emailWhat this does: Creates a mail-enabled security group. This group is the access list - imper.ai will be able to see the calendars of its members, and no one else.
Expected result: The group is created and appears in the Exchange admin center under Recipients > Groups.
NOTE
-Type Security is required. A standard distribution list or a Microsoft 365 Group cannot be used for this purpose, and the access rule in Step 7 will silently match no one if the wrong type is used.
Step 5 - Add the users to be covered
powershell
$users_list | ForEach-Object { Add-DistributionGroupMember -Identity $group_name -Member $_ }What this does: Adds every user from $users_list to the group, granting imper.ai access to their calendars.
Expected result: No output. Confirm the membership:
powershell
Get-DistributionGroupMember -Identity $group_name -ResultSize Unlimited |
Select-Object DisplayName, PrimarySmtpAddress, RecipientTypeEvery address from $users_list should be listed, with its PrimarySmtpAddress shown.
IMPORTANT
Only direct members of the group are covered. If you add another group as a member, that nested group's users are not included, and no warning is shown. Add users individually, or ask your imper.ai contact about scoping to an administrative unit instead.
Step 6 - Retrieve the group's identifier
powershell
$dn = (Get-Group -Identity $group_name).DistinguishedName
$dnWhat this does: Looks up the group's full internal identifier and stores it in $dn, which the next step requires.
Expected result: A long string beginning with CN=imper-calendar-access,OU=.... Storing it in a variable avoids the transcription errors that copying such a value by hand tends to produce.
Step 7 - Create the access rule
powershell
New-ManagementScope -Name $scope_name `
-RecipientRestrictionFilter "MemberOfGroup -eq '$dn'"What this does: Creates a rule that resolves to "the current members of this group", checked each time access is attempted.
Expected result: The rule is created and its name is echoed back.
Because the rule is evaluated in real time, you never need to revisit it. Changing who imper.ai can see is done entirely through group membership from this point on.
IMPORTANT
Confirm the rule resolves to real people. A rule that matches nobody is created successfully and reports no error - it surfaces later only as access being denied for everyone. Check it now:
powershell
Get-Recipient -RecipientPreviewFilter (Get-ManagementScope $scope_name).RecipientFilter |
Select-Object DisplayName, PrimarySmtpAddressEvery group member should be listed. An empty result means $dn was not set correctly in Step 6, or the group is not security-enabled.
Step 8 - Grant imper.ai access, limited to the group
Register imper.ai in Exchange and grant the scoped role:
powershell
New-ServicePrincipal -AppId $app_id `
-ObjectId $sp.Id `
-DisplayName "imper.ai"
New-ManagementRoleAssignment -Name $assignment_name `
-App $app_id `
-Role "Application Calendars.ReadWrite" `
-CustomResourceScope $scope_nameWhat this does: The first command registers imper.ai's identity within Exchange, which keeps its own directory of service principals separate from Entra. The second grants calendar access, restricted to the group from Step 4.
Expected result: Both commands echo back the object they created.
NOTE
One role covers both needs. Calendars.ReadWrite permits creating, reading, updating and deleting calendar events, so it already includes everything Calendars.Read allows. A separate Application Calendars.Read assignment would be redundant.
Verify the Result
powershell
# Every user who should be covered
$users_list | ForEach-Object {
Test-ServicePrincipalAuthorization -Identity $app_id -Resource $_ |
Select-Object @{ n = 'Resource'; e = { $_ } }, RoleName, AllowedResourceScope, InScope
}
# And one user who should NOT be
Test-ServicePrincipalAuthorization -Identity $app_id `
-Resource "someone.else@$organization_domain" | Format-TableWhat this does: Reports whether imper.ai is permitted to access the calendar of each mailbox named.
Expected result:
InScope: Truefor every member of the groupInScope: Falsefor the non-member
Both outcomes are needed to confirm the configuration - the second is what proves the restriction is actually holding.
NOTE
This command reports your configuration immediately. The live permission change takes longer to apply - see Timing below.
Timing
Microsoft caches application permissions, so any change in this guide takes up to 2 hours to take effect. It is often quicker - Microsoft's cache is refreshed after 30 minutes for an application that has been idle, and held for up to 2 hours for one that is actively in use - but plan for the full 2 hours.
During this window, imper.ai may still reach a mailbox you have just excluded, or be unable to reach one you have just added. Test-ServicePrincipalAuthorization bypasses the cache, so it reports the new configuration while the old one is still in force. This is expected, and resolves without intervention.
Changing Who Is Covered
Add or remove group members. Nothing else needs to change - the rule from Step 7 re-reads the membership on every access attempt.
powershell
Add-DistributionGroupMember -Identity $group_name -Member "newuser@$organization_domain"
Remove-DistributionGroupMember -Identity $group_name -Member "olduser@$organization_domain"To replace the whole list at once, update $users_list and re-run Step 5. Allow for the caching window above before any change takes effect.
Removing the Configuration
To return to the standard tenant-wide arrangement, remove the objects in the order below, then grant Calendars.ReadWrite to imper.ai through the usual consent flow in the Azure portal. Paste the Step 1 variable block first if you are in a new session.
powershell
Remove-ManagementRoleAssignment -Identity $assignment_name -Confirm:$false
Remove-ManagementScope -Identity $scope_name -Confirm:$false
Remove-DistributionGroup -Identity $group_name -Confirm:$false
Remove-ServicePrincipal -Identity $app_id -Confirm:$falseThe order matters - a rule cannot be removed while a grant still refers to it, so the grant comes first.
Confirm nothing is left behind:
powershell
Get-ManagementRoleAssignment -Identity $assignment_name -ErrorAction SilentlyContinue
Get-ManagementScope -Identity $scope_name -ErrorAction SilentlyContinue
Get-DistributionGroup -Identity $group_name -ErrorAction SilentlyContinue
Get-ServicePrincipal -Identity $app_id -ErrorAction SilentlyContinueEach should return nothing.
Troubleshooting
"The term 'New-DistributionGroup' is not recognized"
The Exchange commands are added to your session by the sign-in in Step 2. Run Connect-ExchangeOnline first.
"The term 'Get-MgServicePrincipal' is not recognized"
The Graph commands come from a separate module. Run Import-Module Microsoft.Graph.Applications and Connect-MgGraph -Scopes "Application.Read.All" from Step 2.
Get-MgServicePrincipal returns nothing
The Application ID does not match any application in your tenant. Two common causes:
- The value in
$app_idhas a typo, or is the imper.ai Object ID rather than the Application ID. Confirm with your imper.ai contact. - imper.ai has not yet been consented to in your tenant. The application must exist under Enterprise applications before it can be granted a scoped role. Complete the standard consent flow first, then return to this guide.
Confirm what the filter matches:
powershell
Get-MgServicePrincipal -Filter "appId eq '$app_id'" | Select-Object Id, AppId, DisplayNameA command creates an object with a blank or wrong name
The variables from Step 1 are not set in the current session - most often because the window was closed or the session timed out. Paste the Step 1 block again, verify with $group_name, $scope_name, $app_id, then remove any misnamed object and repeat the step.
InScope: False for a user who is in the group
Work through these in order:
powershell
# 1. What does the rule actually say?
Get-ManagementScope -Identity $scope_name | Format-List Name, RecipientFilter, RecipientRoot
# 2. Does it resolve to anyone?
Get-Recipient -RecipientPreviewFilter (Get-ManagementScope $scope_name).RecipientFilter |
Select-Object DisplayName, PrimarySmtpAddress
# 3. Is the group security-enabled, and is the user a direct member?
Get-DistributionGroup -Identity $group_name | Format-List Name, GroupType, RecipientTypeDetails
Get-DistributionGroupMember -Identity $group_name | Select-Object DisplayName, PrimarySmtpAddress
# 4. What is the grant bound to?
Get-ManagementRoleAssignment -Identity $assignment_name |
Format-List Name, Role, App, CustomResourceScope, EnabledIf the identifier stored in the rule does not match the group, correct it:
powershell
$dn = (Get-Group -Identity $group_name).DistinguishedName
Set-ManagementScope -Identity $scope_name -RecipientRestrictionFilter "MemberOfGroup -eq '$dn'"imper.ai still reaches mailboxes outside the group
Either the caching window has not elapsed, or a tenant-wide Calendars.ReadWrite grant is still in place from an earlier consent flow. Check the application's Permissions page under Microsoft Entra ID > Enterprise applications > imper.ai and revoke it - Microsoft treats the two grants as additive.
A user in the group cannot be reached
Confirm they are a direct member with Get-DistributionGroupMember. Users belonging to a nested group are not covered.
"Insufficient permissions" when running a command
Every step requires the Exchange Administrator role or membership in the Organization Management role group.
Related
- Integrating Workday with imper.ai for Hiring - the calendar and meeting permissions in the context of the full Hiring setup
- Microsoft Entra Permissions - the full list of permissions imper.ai requests and why
- Integrations Overview