PowerShell sample for Privileged Identity Management (PIM) for Azure AD Roles
PIM for Azure AD Roles provides Just in Time (JIT) capability for Azure AD Roles. See more at https://docs.microsoft.com/en-us/azure/active-directory/active-directory-privileged-identity-management-getting-started
How cool would it be if I can use the MSGraph PIM api’s to build custom applications. For example, you have multiple roles where you want to activate every day. It would be time consuming to activate them one by one. Instead, you can build a custom app using PowerShell or UI so that you can activate to all of these roles in one shot.
In this blog, I will share a sample to list all your eligible roles and activate or deactivate them. You will also be able to assign someone to a role.
I will share the full source code so you can customize it to suit your needs. Just save this as a .ps1 file and run it with PowerShell.
Note: There is a limitation with MSGraph PIM api's that it wont work if your role requires MFA which should be fixed soon.
Setup
- Create a native AAD application. See /en-us/azure/active-directory/develop/active-directory-integrating-applications
- Grant it the following permissions to the application.
- Read and write privileged access to Azure AD - You will need it if you are going to use the app for PIM for Azure AD Roles
- Read and write privileged access to Azure resources - You will need it if you are going to use the app for PIM for Azure Resources
- Read directory data - You will need it if you are going to read users, etc. from directory like the assignment example in the below source code
- Note than these permissions require Admin consent so you will have to contact the tenant admin to grant these permissions. See https://docs.microsoft.com/en-us/azure/active-directory/application-dev-registration-config-grant-permissions-how-to
- In the below code, update $clientID with your application id and $redirectUri with the redirect uri of the application.
Source code
#Loads Active Directory Authentication Library
function Load-ActiveDirectoryAuthenticationLibrary(){
$moduleDirPath = [Environment]::GetFolderPath("MyDocuments") + "\WindowsPowerShell\Modules"
$modulePath = $moduleDirPath + "\AADGraph"
if(-not (Test-Path ($modulePath+"\Nugets"))) {New-Item -Path ($modulePath+"\Nugets") -ItemType "Directory" | out-null}
$adalPackageDirectories = (Get-ChildItem -Path ($modulePath+"\Nugets") -Filter "Microsoft.IdentityModel.Clients.ActiveDirectory*" -Directory)
if($adalPackageDirectories.Length -eq 0){
Write-Host "Active Directory Authentication Library Nuget doesn't exist. Downloading now ..." -ForegroundColor Yellow
if(-not(Test-Path ($modulePath + "\Nugets\nuget.exe")))
{
Write-Host "nuget.exe not found. Downloading from https://www.nuget.org/nuget.exe ..." -ForegroundColor Yellow
$wc = New-Object System.Net.WebClient
$wc.DownloadFile("https://www.nuget.org/nuget.exe",$modulePath + "\Nugets\nuget.exe");
}
$nugetDownloadExpression = $modulePath + "\Nugets\nuget.exe install Microsoft.IdentityModel.Clients.ActiveDirectory -Version 2.14.201151115 -OutputDirectory " + $modulePath + "\Nugets | out-null"
Invoke-Expression $nugetDownloadExpression
}
$adalPackageDirectories = (Get-ChildItem -Path ($modulePath+"\Nugets") -Filter "Microsoft.IdentityModel.Clients.ActiveDirectory*" -Directory)
$ADAL_Assembly = (Get-ChildItem "Microsoft.IdentityModel.Clients.ActiveDirectory.dll" -Path $adalPackageDirectories[$adalPackageDirectories.length-1].FullName -Recurse)
$ADAL_WindowsForms_Assembly = (Get-ChildItem "Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll" -Path $adalPackageDirectories[$adalPackageDirectories.length-1].FullName -Recurse)
if($ADAL_Assembly.Length -gt 0 -and $ADAL_WindowsForms_Assembly.Length -gt 0){
Write-Host "Loading ADAL Assemblies ..." -ForegroundColor Green
[System.Reflection.Assembly]::LoadFrom($ADAL_Assembly[0].FullName) | out-null
[System.Reflection.Assembly]::LoadFrom($ADAL_WindowsForms_Assembly.FullName) | out-null
return $true
}
else{
Write-Host "Fixing Active Directory Authentication Library package directories ..." -ForegroundColor Yellow
$adalPackageDirectories | Remove-Item -Recurse -Force | Out-Null
Write-Host "Not able to load ADAL assembly. Delete the Nugets folder under" $modulePath ", restart PowerShell session and try again ..."
return $false
}
}
#Acquire AAD token
function AcquireToken($mfa){
$clientID = "dabc52c4-106b-4179-9df2-2f791f44ba14"
$redirectUri = "https://pimmsgraph"
$authority = "https://login.microsoftonline.com/common"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority,$false
if($mfa)
{
$authResult = $authContext.AcquireToken("https://graph.microsoft.com",$ClientID,$redirectUri,[Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Auto, [Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier]::AnyUser, "amr_values=mfa")
Set-Variable -Name mfaDone -Value $true -Scope Global
}
else
{
$authResult = $authContext.AcquireToken("https://graph.microsoft.com",$ClientID,$redirectUri,[Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Always)
}
if($authResult -ne $null)
{
Write-Host "User logged in successfully ..." -ForegroundColor Green
}
Set-Variable -Name headerParams -Value @{'Authorization'="$($authResult.AccessTokenType) $($authResult.AccessToken)"} -Scope Global
Set-Variable -Name assigneeId -Value $authResult.UserInfo.UniqueId -Scope Global
}
#Gets my jit assignments
function MyJitAssignments(){
$url = $serviceRoot + "privilegedRoleAssignments/my?`$expand=roleInfo&`$filter=isElevated+eq+false"
Write-Host $url
$response = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url -Method Get
$assignments = ConvertFrom-Json $response.Content
Write-Host ""
Write-Host "Role assignments..." -ForegroundColor Green
$i = 0
$obj = @()
foreach ($assignment in $assignments.value)
{
$item = New-Object psobject -Property @{
Id = ++$i
RoleAssignmentId = $assignment.id
RoleId = $assignment.roleInfo.id
RoleName = $assignment.roleInfo.name
UserId = $assignment.userid
}
$obj = $obj + $item
}
return $obj
}
#Gets my active assignments
function MyActivatedAssignments(){
$url = $serviceRoot + "privilegedRoleAssignments/my?`$expand=roleInfo&`$filter=isElevated+eq+true+and+expirationDateTime+ne+null"
Write-Host $url
$response = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url -Method Get
$assignments = ConvertFrom-Json $response.Content
Write-Host ""
Write-Host "Role assignments..." -ForegroundColor Green
$i = 0
$obj = @()
foreach ($assignment in $assignments.value)
{
$item = New-Object psobject -Property @{
Id = ++$i
RoleAssignmentId = $assignment.id
RoleId = $assignment.roleInfo.id
RoleName = $assignment.roleInfo.name
UserId = $assignment.userid
ExpirationDateTime = $assignment.expirationDateTime
}
$obj = $obj + $item
}
return $obj
}
#List roles
function ListRoles(){
$url = $serviceRoot + "privilegedRoles?&`$orderby=name"
Write-Host $url
$response = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url -Method Get
$roles = ConvertFrom-Json $response.Content
$i = 0
$obj = @()
foreach ($role in $roles.value)
{
$item = New-Object psobject -Property @{
Id = ++$i
RoleId = $role.id
RoleName = $role.name
}
$obj = $obj + $item
}
return $obj
}
#List Assignment
function ListAssignmentsWithFilter($roleId){
$url = $serviceRoot + "privilegedRoleAssignments?`$expand=roleInfo&`$filter=roleId+eq+'" + $roleId + "'"
Write-Host $url
$response = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url -Method Get
$roleAssignments = ConvertFrom-Json $response.Content
$i = 0
$obj = @()
foreach ($roleAssignment in $roleAssignments.value)
{
$item = New-Object psobject -Property @{
Id = ++$i
RoleAssignmentId = $roleAssignment.id
RoleId = $roleAssignment.roleInfo.id
RoleName = $roleAssignment.roleInfo.name
IsElevated = $roleAssignment.isElevated
ExpirationDateTime = $roleAssignment.expirationDateTime
UserId = $roleAssignment.userId
}
$obj = $obj + $item
}
return $obj
}
#List Users
function ListUsers($user_search){
$url = $MSGraphRoot + "users?`$filter=startswith(displayName,'" + $user_search + "')"
Write-Host $url
$response = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url -Method Get
$users = ConvertFrom-Json $response.Content
$i = 0
$obj = @()
foreach ($user in $users.value)
{
$item = New-Object psobject -Property @{
Id = ++$i
UserId = $user.id
UserName = $user.DisplayName
}
$obj = $obj + $item
}
return $obj
}
#Activates the user
function Activate($isRecursive = $false){
if($isRecursive -eq $false)
{
$assignments = MyJitAssignments
$assignments | Format-Table -AutoSize -Wrap Id,RoleName
$choice = Read-Host "Enter Id to activate"
$hours = Read-Host "Enter Activation duration in hours"
$reason = Read-Host "Enter Reason"
}
$roleId = $assignments[$choice-1].RoleId
$url = $serviceRoot + "privilegedRoles('" + $roleId + "')/selfActivate"
$postParams = '{"duration":"' + $hours + '","reason":"' + $reason + '"}'
write-Host $postParams
try
{
$response = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url -Method Post -ContentType "application/json" -Body $postParams
Write-Host "Role activated successfully ..." -ForegroundColor Green
}
catch
{
$stream = $_.Exception.Response.GetResponseStream()
$stream.Position = 0;
$streamReader = New-Object System.IO.StreamReader($stream)
$err = $streamReader.ReadToEnd()
$streamReader.Close()
$stream.Close()
if($mfaDone -eq $false -and $err.Contains("MfaRule"))
{
Write-Host "Prompting the user for mfa ..." -ForegroundColor Green
AcquireToken true
Activate $true
}
else
{
Write-Host $err -ForegroundColor Red
}
}
}
#Deactivates the user
function Deactivate($isRecursive = $false){
if($isRecursive -eq $false)
{
$assignments = MyActivatedAssignments
$assignments | Format-Table -AutoSize -Wrap Id,RoleName,ExpirationDateTime
$choice = Read-Host "Enter Id to deactivate"
}
$roleId = $assignments[$choice-1].RoleId
$url = $serviceRoot + "privilegedRoles('" + $roleId + "')/selfDeactivate"
$response = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url -Method Post -ContentType "application/json"
Write-Host "Role deactivated successfully ..." -ForegroundColor Green
}
#List RoleAssignment
function ListAssignment(){
#List and Pick a role
$roles = ListRoles
$roles | Format-Table -AutoSize -Wrap Id, RoleName, RoleId
$role_choice = Read-Host "Pick a role Id"
$roleId = $roles[$role_choice-1].RoleId
write-Host $roleId
#List Member
$roleAssignments = ListAssignmentsWithFilter $roleId
$roleAssignments | Format-Table -AutoSize -Wrap Id, RoleName, UserId, IsElevated, ExpirationDateTime
}
#Assign a user to Eligible
function AssignmentEligible() {
#List and Pick a role
$roles = ListRoles
$roles | Format-Table -AutoSize -Wrap Id, RoleName
$role_choice = Read-Host "Pick a role Id"
$roleId = $roles[$role_choice-1].RoleId
write-Host $roleId
#Search user by Name, and pick a user
$user_search = Read-Host "user Name start with..."
$users = ListUsers($user_search)
$users | Format-Table -AutoSize -Wrap Id, UserName, UserId
$user_choice = Read-Host "Pick a user Id"
$userId = $users[$user_choice-1].UserId
$url = $serviceRoot + "privilegedRoleAssignments"
$postParams = '{"roleId":"' + $roleId + '","userId":"' + $userId + '"}'
write-Host $postParams
$response = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url -Method Post -ContentType "application/json" -Body $postParams
Write-Host "Assignment added successfully ..." -ForegroundColor Green
}
#Show menu
function ShowMenu(){
Write-Host ""
Write-Host "Azure AD JIT - PowerShell Menu v1.0"
Write-Host " 1. List your eligible role assignments"
Write-Host " 2. Activate an eligible role"
Write-Host " 3. Deactivate an active role"
Write-Host " 4. List Assignment against a role"
Write-Host " 5. Assign a user to a role"
Write-Host " 6. Exit"
}
############################################################################################################################################################################
$global:serviceRoot = "https://graph.microsoft.com/beta/"
$global:MSGraphRoot = "https://graph.microsoft.com/v1.0/"
$global:headerParams = ""
$global:assigneeId = ""
$global:mfaDone = $false;
Load-ActiveDirectoryAuthenticationLibrary
AcquireToken
do
{
ShowMenu
#Write-Host "Enter your selection"
$input = Read-Host "Enter your selection"
switch ($input)
{
'1'
{
$assignments = MyJitAssignments
$assignments | Format-Table -AutoSize -Wrap Id,RoleName
}
'2'
{
Activate
}
'3'
{
Deactivate
}
'4'
{
ListAssignment
}
'5'
{
AssignmentEligible
}
'6'
{
return
}
}
}
until ($input -eq '6')
Write-Host ""