Framework/Core/PIM/PIMScript.ps1


#Acquire Access token
class PIM: CommandBase
{
    hidden $APIroot =  [string]::Empty
    hidden $headerParams = "";
    hidden $UserId = "";
    hidden  $AccessToken="";
    hidden $AccountId="" ;

    PIM([string] $subscriptionId, [InvocationInfo] $invocationContext)
    : Base([string] $subscriptionId, [InvocationInfo] $invocationContext)
    {
        $this.AccessToken = "";
        $this.AccountId="";
        $this.APIroot = "https://api.azrbac.mspim.azure.com/api/v2/privilegedAccess/azureResources/";
    }
  
#Acquire Access token
 AcquireToken()
{
    # Using helper method to get current context and access token
    $ResourceAppIdURI = [WebRequestHelper]::GetServiceManagementUrl()
    $this.AccessToken = [Helpers]::GetAccessToken($ResourceAppIdURI);
    $this.AccountId = [Helpers]::GetCurrentSessionUser()
    $this.UserId = (Get-AzADUser -UserPrincipalName  $this.AccountId).Id
    $this.headerParams= @{'Authorization'="Bearer $($this.AccessToken)"}
    
}

#Gets the jit assignments for logged-in user
hidden [PSObject] MyJitAssignments($active)
{
    $urlme = "";
    if($active)
    {
    $urlme = $this.APIroot + "/roleAssignments?`$expand=linkedEligibleRoleAssignment,subject,roleDefinition(`$expand=resource)&`$filter=(subject/id%20eq%20%27$($this.UserId)%27)+and+(assignmentState%20eq%20%27Eligible%27)"
    }
    else
     {
        $urlme = $this.APIroot + "/roleAssignments?`$expand=linkedEligibleRoleAssignment,subject,roleDefinition(`$expand=resource)&`$filter=(subject/id%20eq%20%27$($this.UserId)%27)+and+(assignmentState%20eq%20%27Active%27)" 
     }
    $response = Invoke-WebRequest -UseBasicParsing -Headers $this.headerParams -Uri $urlme -Method Get
    $assignments = ConvertFrom-Json $response.Content
    $assignments = $assignments.value
    $obj = @()
    Write-Host ""
    if(($assignments | Measure-Object).Count -gt 0)
    {
        $i = 0
        foreach ($assignment in $assignments)
        {
            $item = New-Object psobject -Property @{
            Id = ++$i
            IdGuid =  $assignment.id
            ResourceId =  $assignment.roleDefinition.resource.id
            OriginalId =  $assignment.roleDefinition.resource.externalId
            ResourceName =  $assignment.roleDefinition.resource.displayName
            ResourceType =  $assignment.roleDefinition.resource.type
            RoleId = $assignment.roleDefinition.id
            RoleName = $assignment.roleDefinition.displayName
            ExpirationDate = $assignment.endDateTime
            SubjectId = $assignment.subject.id
            }
            $obj = $obj + $item
        }
    }
         
    return $obj
}


#List resources
hidden [PSObject] ListResources()
{
    $url = $this.APIroot + "/resources?`$select=id,displayName,type&`$filter=(type%20eq%20%27subscription%27)&`$orderby=displayName&`$top=10&1540887279129" 
    # Write-Host $url

    $response = Invoke-WebRequest -UseBasicParsing -Headers $this.headerParams -Uri $url -Method Get
    $resources = ConvertFrom-Json $response.Content
    $i = 0
    $obj = @()
    foreach ($resource in $resources.value)
    {
        $item = New-Object psobject -Property @{
        Id = ++$i
        ResourceId =  $resource.id
        ResourceName =  $resource.DisplayName
        Type =  $resource.type
        ExternalId =  $resource.externalId
    }
    $obj = $obj + $item
}

return $obj
}

#List roles
hidden [PSObject] ListRoles($resourceId)
{
    $url =$this.APIroot + "resources/" + $resourceId + "/roleDefinitions?`$select=id,displayName,type,templateId,resourceId,externalId,subjectCount,eligibleAssignmentCount,activeAssignmentCount&`$orderby=activeAssignmentCount%20desc"
    # Write-Host $url

    $response = Invoke-WebRequest -UseBasicParsing -Headers $this.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
        RoleDefinitionId =  $role.id
        RoleName =  $role.DisplayName
        SubjectCount = $role.SubjectCount
    }
    $obj = $obj + $item
    }

    return $obj 
}

#List Assignment
hidden [PSObject] ListAssignmentsWithFilter($resourceId, $roleDefinitionId)
{
    $url = $this.APIroot + "resources/" + $resourceId + "`/roleAssignments?`$expand=subject,roleDefinition(`$expand=resource)&`$filter=(roleDefinition/id+eq+'" + $roleDefinitionId + "')"
    # Write-Host $url

    $response = Invoke-WebRequest -UseBasicParsing -Headers $this.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
        ResourceId =  $roleAssignment.roleDefinition.resource.id
        OriginalId =  $roleAssignment.roleDefinition.resource.externalId
        ResourceName =  $roleAssignment.roleDefinition.resource.displayName
        ResourceType =  $roleAssignment.roleDefinition.resource.type
        RoleId = $roleAssignment.roleDefinition.id
        IsPermanent=$roleAssignment.IsPermanent
        RoleName = $roleAssignment.roleDefinition.displayName
        ExpirationDate = $roleAssignment.endDateTime
        SubjectId = $roleAssignment.subject.id
        UserName = $roleAssignment.subject.displayName
        AssignmentState = $roleAssignment.AssignmentState
    }
    $obj = $obj + $item
}

return $obj
}

#List Users
hidden [PSObject]  ListUsers($user_search)
{
    $url = $this.APIroot + "users?`$filter=startswith(displayName,'" + $user_search + "')"
    # Write-Host $url

    $response = Invoke-WebRequest -UseBasicParsing -Headers $this.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
hidden Activate()
{
    
    $assignments =$this.MyJitAssignments(1)
    if(($assignments | Measure-Object).Count -gt 0)
    {
        $this.PublishCustomMessage("Role assignments:",[MessageType]::Default)
        $this.PublishCustomMessage("");
        $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
        $this.PublishCustomMessage($($assignments | Format-Table -AutoSize -Wrap Id,RoleName,ResourceName,ResourceType,ExpirationDate | Out-String),[MessageType]::Default)
        $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
        $this.PublishCustomMessage("")
        Write-Host " Enter Id to activate: " -ForegroundColor Cyan -NoNewline 
        $choice = Read-Host 
        while($choice -notin $assignments.Id)
        {
           Write-Host " Invalid input" -ForegroundColor Yellow 
           Write-Host " Enter Id to activate: " -ForegroundColor Cyan -NoNewline
          $choice = Read-Host 
        }

        try
        {
            Write-Host " Enter activation duration in hours between 1 to 8 hours: " -ForegroundColor Cyan -NoNewline
            [int] $hours =  Read-Host
            while($hours -lt 1 -or $hours -gt 8)
            {
                Write-Host " Invalid input" -ForegroundColor Yellow
                Write-Host " Enter activation duration in hours between 1 to 8 hours: " -ForegroundColor Cyan -NoNewline
               [int] $hours = Read-Host 
            }
        }
        catch
        {
            Write-Host " Please enter a valid integer value: " -ForegroundColor Yellow -NoNewline
            [int] $hours = Read-Host
            while($hours -lt 1 -or $hours -gt 8)
            {
                Write-Host " Invalid input" -ForegroundColor Yellow
                Write-Host " Enter activation duration in hours between 1 to 8 hours: " -ForegroundColor Cyan -NoNewline
               [int] $hours = Read-Host
            } 
        }
        Write-Host " Enter the reason for activation of the role: " -ForegroundColor Cyan -NoNewline
        $reason = Read-Host
        $id = $assignments[$choice-1].IdGuid
        $resourceId = $assignments[$choice-1].ResourceId
        $roleDefinitionId = $assignments[$choice-1].RoleId
        $subjectId = $assignments[$choice-1].SubjectId
        $url = $this.APIroot + "roleAssignmentRequests "
        $postParams = '{"roleDefinitionId":"'+$roleDefinitionId+'","resourceId":"'+$resourceId+'","subjectId":"'+$subjectId+'","assignmentState":"Active","type":"UserAdd","reason":"'+$reason+'","schedule":{"type":"Once","startDateTime":"'+(Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")+'","duration":"PT' + $hours + 'H"},"linkedEligibleRoleAssignmentId":"'+$id+'"}'
   

        try
        {
            $response = Invoke-WebRequest -UseBasicParsing -Headers $this.headerParams -Uri $url -Method Post -ContentType "application/json" -Body $postParams
            if($response.StatusCode -eq 201)
            {
                $this.PublishCustomMessage("Activation request queued successfully ...",[MessageType]::Update);
            }
             $recursive = $false
        }
        catch
        {
            $this.PublishCustomMessage($_.Exception.Message,[MessageType]::Error)
        }
    }
    else
    {
        Write-Host " No eligible role found for the logged in context" -ForegroundColor Yellow
    }
}

#Deactivates the user
hidden Deactivate()
{
         
        $assignments = $this.MyJitAssignments(0) | Where-Object{-not [string]::IsNullorEmpty($_.ExpirationDate)}
        if(($assignments | Measure-Object).Count -gt 0)
        {
            $this.PublishCustomMessage("Role assignments: ")
            $this.PublishCustomMessage("")
            $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
            $this.PublishCustomMessage($($assignments | Format-Table -AutoSize -Wrap Id,RoleName,ResourceName,ResourceType,ExpirationDate | Out-String),[MessageType]::Default)
            $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
            $this.PublishCustomMessage("")
            Write-Host " Pick Id to deactivate: " -ForegroundColor Cyan -NoNewline
            $choice = Read-Host
            while($choice -notin $assignments.Id)
            {
                 Write-Host " Invalid input" -ForegroundColor Yellow
                 Write-Host " Pick Id to deactivate: " -ForegroundColor Cyan -NoNewline
                $choice = Read-Host 
            }
            $id = $assignments[$choice-1].IdGuid
            $resourceId = $assignments[$choice-1].ResourceId
            $roleDefinitionId = $assignments[$choice-1].RoleId
            $subjectId = $assignments[$choice-1].SubjectId
            $url = $this.APIroot + "/roleAssignmentRequests "
            $postParams = '{"roleDefinitionId":"'+$roleDefinitionId+'","resourceId":"'+$resourceId+'","subjectId":"'+$subjectId+'","assignmentState":"Active","type":"UserRemove","linkedEligibleRoleAssignmentId":"'+$id+'"}'
           try
           {
                $response = Invoke-WebRequest -UseBasicParsing -Headers $this.headerParams -Uri $url -Method Post -ContentType "application/json" -Body $postParams
                if($response.StatusCode -eq '201')
                {
                     $this.PublishCustomMessage("Role deactivated successfully ... ",[MessageType]::Update);
                }
            
            }
            catch
            {
                $this.PublishCustomMessage($_.Exception.Message,[MessageType]::Error)
            }
        }
        else
        {
            $this.PublishCustomMessage("No Active assignments found.",[MessageType]::Warning);
        }
    

}

#List RoleAssignment
ListAssignment()
{
    #List and Pick resource
    $resources = $this.ListResources()
    if(($resources | Measure-Object).Count -gt 0)
    {
        $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
        $this.PublishCustomMessage($($resources | Format-Table -AutoSize -Wrap Id, ResourceName, Type, ExternalId | Out-String),[MessageType]::Default)
        $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
        $this.PublishCustomMessage("")
        Write-Host " Pick a resource Id for assigment: " -ForegroundColor Cyan -NoNewline
        $res_choice = Read-Host 
        while($res_choice -notin $resources.Id)
        {
             Write-Host " Invalid input" -ForegroundColor Yellow
             Write-Host " Pick a resource Id for assigment: " -ForegroundColor Cyan -NoNewline
            $res_choice = Read-Host            
        }
        $resourceId = $resources[$res_choice-1].ResourceId

        #List and Pick a role
        $roles = $this.ListRoles($resourceId) |  Where-Object{ $_.SubjectCount -gt 0}
        $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
        $this.PublishCustomMessage($($roles | Format-Table -AutoSize -Wrap Id, RoleName, RoleDefinitionId | Out-String),[MessageType]::Default)
        $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
        $this.PublishCustomMessage("")
        Write-Host " Pick a role Id: " -ForegroundColor Cyan -NoNewline
        $role_choice = Read-Host
        while($role_choice -notin $roles.Id)
        {
             Write-Host " Invalid input" -ForegroundColor Yellow
             Write-Host " Pick a role Id: " -ForegroundColor Cyan -NoNewline
            $role_choice = Read-Host 
        }
        $roleDefinitionId = $roles[$role_choice-1].RoleDefinitionId
        #write-Host $roleDefinitionId

        #List Member
        $roleAssignments=$this.ListAssignmentsWithFilter($resourceId,$roleDefinitionId)
        $permanentAssignment=$roleAssignments | Where-Object{$_.IsPermanent -eq $true}
        if(($permanentAssignment | Measure-Object).Count -gt 0)
        {
            $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
            $this.PublishCustomMessage($($permanentAssignment | Format-Table UserName, ResourceType,ResourceId | Out-String),[MessageType]::Default)
            $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
            $this.PublishCustomMessage("")
        }
        else
        {
            $this.PublishCustomMessage(" No permanent assignments found for this combination.",[MessageType]::Warning);
        }
    }
    else
    {
        $this.PublishCustomMessage("No active assignments found for the current logged in context.", [MessageType]::Warning )
    }
}

#Assign a user to Eligible Role
hidden AssignmentEligible() 
{
    #List and Pick resource
    $resources = $this.ListResources();
    $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
    $this.PublishCustomMessage($($resources | Format-Table -AutoSize -Wrap Id, ResourceName, Type, ExternalId | Out-String),[MessageType]::Default)
    $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
    $this.PublishCustomMessage("")
    if(($resources | Measure-Object).Count -gt 0)
    {
            Write-Host " Pick a resource Id for assigment: " -ForegroundColor Cyan -NoNewline
            $res_choice = Read-Host 
            while($res_choice -notin $resources.Id)
            {
                 Write-Host " Invalid input" -ForegroundColor Yellow
                 Write-Host " Pick a resource Id for assigment: " -ForegroundColor Cyan -NoNewline
                $res_choice = Read-Host 
            }
            $resourceId = $resources[$res_choice-1].ResourceId

            #List and Pick a role
            $roles = $this.ListRoles($resourceId)
            $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
            $this.PublishCustomMessage($($roles | Format-Table -AutoSize -Wrap Id, RoleName, RoleDefinitionId | Out-String),[MessageType]::Default)
            $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
            $this.PublishCustomMessage("")
            Write-Host " Pick a role Id: " -ForegroundColor Cyan -NoNewline
            $role_choice = Read-Host 
             while($role_choice -notin $roles.Id)
            {
                Write-Host " Invalid input" -ForegroundColor Yellow
                Write-Host " Pick a role Id: " -ForegroundColor Cyan -NoNewline
                $role_choice = Read-Host 
            }
            $roleDefinitionId = $roles[$role_choice-1].RoleDefinitionId
    
            #Get Object Id of usesr by Name, and get input for allowed time of role assignment
            Write-Host " Please enter the Principal Name ( e.g. 'xyz@contoso.com') of the user to whom role has to be assigned: " -ForegroundColor Cyan -NoNewline
            $user_search = Read-Host 
            try
            {
                $users = Get-AzADUser -UserPrincipalName $user_search
                while(($users | Measure-Object).Count -ne 1)
                {
                    $this.PublishCustomMessage("Unable to fetch details of the principal name provided, please make sure to enter the correct values.", [MessageType]::Warning)
                    Write-Host " Please enter the Principal Name ( e.g. 'xyz@contoso.com') of the user to whom role has to be assigned: " -ForegroundColor Cyan -NoNewline
                    $user_search = Read-Host 
                    $users = Get-AzADUser -UserPrincipalName $user_search
                }
            }
            catch
            {
                $this.PublishCustomMessage("Unable to fetch details of the principal name provided, please make sure to enter the correct values.", [MessageType]::Warning)
                return;
            }
            $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
            $this.PublishCustomMessage($($users | Format-Table -Property  * | Out-String),[MessageType]::Default);
            $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
            $this.PublishCustomMessage("")
            [int]$days=90
            $subjectId = $users.Id
            try
            {
                Write-Host " Enter the period in days between 1 to 90 days for role assignment: " -ForegroundColor Cyan -NoNewline
                [int]$days= Read-Host 
                while($days -gt 90 -or $days -lt 1)
                {
                    Write-Host " Invalid input" -ForegroundColor Yellow
                    Write-Host " Enter the period in days between 1 to 90 days for role assignment: " -ForegroundColor Cyan -NoNewline
                    $days= Read-Host 
                }
            }
            catch
            {
                
                Write-Host " Please enter a integer value between 1 to 90" -ForegroundColor Yellow -NoNewline
                Write-Host " Enter the period in days between 1 to 90 days for role assignment: " -ForegroundColor Cyan
                [int]$days= Read-Host 
                while($days -gt 90 -or $days -lt 1)
                {
                    Write-Host " Invalid input" -ForegroundColor Yellow
                    Write-Host " Enter the period in days between 1 to 90 days for role assignment: " -ForegroundColor Cyan -NoNewline
                    $days= Read-Host 
                } 
            }

            $url = $this.APIroot+"/roleAssignmentRequests"
            # Update end time
            $ts = New-TimeSpan -Days $days
            $postParams = '{"assignmentState":"Eligible","type":"AdminAdd","reason":"Assign","roleDefinitionId":"' + $roleDefinitionId + '","resourceId":"' + $resourceId + '","subjectId":"' + $subjectId + '","schedule":{"startDateTime":"' + (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") + '","endDateTime":"' + ((get-date) + $ts).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") + '","type":"Once"}}'
    
            try
            {
                $response = Invoke-WebRequest -UseBasicParsing -Headers $this.headerParams -Uri $url -Method Post -ContentType "application/json" -Body $postParams
                if($response.StatusCode -eq 201)
                {
                $this.PublishCustomMessage("Assignment request queued successfully ...",[MessageType]::Update);
                }
                $recursive = $false
            }
            catch
            {
                $this.PublishCustomMessage($_.Exception.Message,[MessageType]::Error)               
            }
    }
    else
    {
       $this.PublishCustomMessage("You are not elligible to assign a role. If you have recently elevated/activated your permissions, please run Connect-AzAccount and re-run the script.",[MessageType]::Warning)
    }
}

 #Show menu
ShowMenu()
{
            $this.PublishCustomMessage("")
            $this.PublishCustomMessage("`n###################################################################################")
            $this.PublishCustomMessage("")
            $this.PublishCustomMessage("Azure PIM Assignment Menu")
            $this.PublishCustomMessage(" 1. List your eligible role assignments")
            $this.PublishCustomMessage(" 2. Activate an eligible role" )
            $this.PublishCustomMessage(" 3. Deactivate an active role" )
            $this.PublishCustomMessage(" 4. Assign a role to user" )
            $this.PublishCustomMessage(" 5. Check permanent access on subscription for a role")
            $this.PublishCustomMessage(" 6. Exit")
            $this.PublishCustomMessage("")
            $this.PublishCustomMessage("`n###################################################################################")
}

hidden [void] PIMScript()
{
    try
    {
        $this.AcquireToken();
    }
    catch
    {
        Write-Host "Unable to fetch access token. Run Connect-AzAccount and then execute this command" -ForegroundColor Red
        return;
    }  
     
    do
        {
            $this.ShowMenu();
            Write-Host " Enter your selection: " -ForegroundColor Cyan -NoNewline
            $input = Read-Host 
            switch ($input)
                {
                    '1'
                    {
                        $assignments = $this.MyJitAssignments(1)
                        if(($assignments | Measure-Object).Count -gt 0)
                        {
                        $this.PublishCustomMessage("Role assignments:",[MessageType]::Default)
                        $this.PublishCustomMessage("");
                        $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
                        $this.PublishCustomMessage(($assignments | Format-Table -AutoSize Id,RoleName,ResourceName,ResourceType,ExpirationDate| Out-String),[MessageType]::Default)
                        $this.PublishCustomMessage([Constants]::SingleDashLine,[MessageType]::Default)
                        $this.PublishCustomMessage("");
                        }
                        else
                        {
                            $this.PublishCustomMessage("No eligible roles found for the current login",[MessageType]::Warning);
                        }
                    }
                    '2'
                    {
                        $this.Activate()
                    }
                    '3'
                    {
                        $this.Deactivate()
                    }
                    '4'
                    {
                        $this.AssignmentEligible()
                    }
                    '5'
                    {
                        $this.ListAssignment()
                    }
                    '6'
                    {
                        return
                    }
                }
            
        }
        while ($input -gt 6 -or ($input -lt 1) )
    
             
}     



}