EasyPIM.psm1

<#
      .Synopsis
       Retrieve all role policies
      .Description
       Get all roles then for each get the policy
      .Parameter scope
       Scope to look at
      .Example
        PS> Get-AllPolicies -scope "subscriptions/$subscriptionID"
 
        Get all roles then for each get the policy
      .Link
      
      .Notes
#>

function Get-AllPolicies {
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
  [CmdletBinding()]
  param (
      [Parameter()]
      [string]
      $scope
  )
    
    $ARMhost = "https://management.azure.com"
    $ARMendpoint = "$ARMhost/$scope/providers/Microsoft.Authorization"
    $restUri = "$ARMendpoint/roleDefinitions?`$select=roleName&api-version=2022-04-01"

    write-verbose "Getting All Policies at $restUri"
    $response = Invoke-ARM -restURI $restUri -Method 'GET' -Body $null
    Write-Verbose $response
    $roles = $response | ForEach-Object {
        $_.value.properties.roleName
    }
    return $roles
}


<#
    .Synopsis
        Get rules for the role $rolename at the specified scope
    .Description
        will convert the json rules to a PSCustomObject
    .Parameter scope
        scope
    .Parameter rolename
        list of the role to check
    .Parameter copyfrom
        $true if this function is invoked for the Copy-PIMAzureReourcePolicy, we will parse the rules differently
    .Example
        PS> get-config -scope $scop -rolename role1
 
        Get the policy of the role role1 at the specified scope
      
    .Notes
        Author: Loïc MICHEL
        Homepage: https://github.com/kayasax/EasyPIM
     #>

function get-config ($scope, $rolename, $copyFrom = $null) {

    $ARMhost = "https://management.azure.com"
    $ARMendpoint = "$ARMhost/$scope/providers/Microsoft.Authorization"
    try {

        
        # 1 Get ID of the role $rolename assignable at the provided scope
        $restUri = "$ARMendpoint/roleDefinitions?api-version=2022-04-01&`$filter=roleName eq '$rolename'"

        write-verbose " #1 Get role definition for the role $rolename assignable at the scope $scope at $restUri"
        #$response = Invoke-RestMethod -Uri $restUri -Method Get -Headers $authHeader -verbose:$false
        $response = Invoke-ARM -restURI $restUri -method "get" -body $null
        $roleID = $response.value.id
        #if ($null -eq $roleID) { throw "An exception occured : can't find a roleID for $rolename at scope $scope" }
        Write-Verbose ">> RodeId = $roleID"

        if( ($roleID -eq "") -or ($null -eq $roleID)){
            Log "Error getting config of $rolename"
            #continue with other roles
             return
        }

        # 2 get the role assignment for the roleID found at #1
        $restUri = "$ARMendpoint/roleManagementPolicyAssignments?api-version=2020-10-01&`$filter=roleDefinitionId eq '$roleID'"
        write-verbose " #2 Get the Assignment for $rolename at $restUri"
        #$response = Invoke-RestMethod -Uri $restUri -Method Get -Headers $authHeader -verbose:$false
        $response = Invoke-ARM -restURI $restUri -Method Get
        $policyId = $response.value.properties.policyId #.split('/')[-1]
        Write-Verbose ">> policy ID = $policyId"

        # 3 get the role policy for the policyID found in #2
        $restUri = "$ARMhost/$policyId/?api-version=2020-10-01"
        write-verbose " #3 get role policy at $restUri"
        #$response = Invoke-RestMethod -Uri $restUri -Method Get -Headers $authHeader -verbose:$false
        $response = Invoke-ARM -restURI $restUri -Method Get

        #Write-Verbose "copy from = $copyFrom"
        if ($null -ne $copyFrom) {
            Invoke-RestMethod -Uri $restUri -Method Get -Headers $authHeader -verbose:$false -OutFile "$_scriptPath\temp.json"
            $response = Get-Content "$_scriptPath\temp.json"
            $response = $response -replace '^.*"rules":\['
            $response = $response -replace '\],"effectiveRules":.*$'
            Remove-Item "$_scriptPath\temp.json"
            return $response
        }
      
        #$response
        # Get config values in a new object:

        # Maximum end user activation duration in Hour (PT24H) // Max 24H in portal but can be greater
        $_activationDuration = $response.properties.rules | Where-Object { $_.id -eq "Expiration_EndUser_Assignment" } | Select-Object -ExpandProperty maximumduration
        # End user enablement rule (MultiFactorAuthentication, Justification, Ticketing)
        $_enablementRules = $response.properties.rules | Where-Object { $_.id -eq "Enablement_EndUser_Assignment" } | Select-Object -expand enabledRules
        # approval required
        $_approvalrequired = $($response.properties.rules | Where-Object { $_.id -eq "Approval_EndUser_Assignment" }).setting.isapprovalrequired
        # approvers
        $approvers = $($response.properties.rules | Where-Object { $_.id -eq "Approval_EndUser_Assignment" }).setting.approvalStages.primaryApprovers
        $approvers | ForEach-Object {
            $_approvers += '@{"id"="' + $_.id + '";"description"="' + $_.description + '";"userType"="' + $_.userType + '"},'
        }

        # permanent assignmnent eligibility
        $_eligibilityExpirationRequired = $response.properties.rules | Where-Object { $_.id -eq "Expiration_Admin_Eligibility" } | Select-Object -expand isExpirationRequired
        if ($_eligibilityExpirationRequired -eq "true") {
            $_permanantEligibility = "false"
        }
        else {
            $_permanantEligibility = "true"
        }
        # maximum assignment eligibility duration
        $_maxAssignmentDuration = $response.properties.rules | Where-Object { $_.id -eq "Expiration_Admin_Eligibility" } | Select-Object -expand maximumDuration
        
        # pemanent activation
        $_activeExpirationRequired = $response.properties.rules | Where-Object { $_.id -eq "Expiration_Admin_Assignment" } | Select-Object -expand isExpirationRequired
        if ($_activeExpirationRequired -eq "true") {
            $_permanantActiveAssignment = "false"
        }
        else {
            $_permanantActiveAssignment = "true"
        }
        # maximum activation duration
        $_maxActiveAssignmentDuration = $response.properties.rules | Where-Object { $_.id -eq "Expiration_Admin_Assignment" } | Select-Object -expand maximumDuration

        #################
        # Notifications #
        #################

        # Notification Eligibility Alert (Send notifications when members are assigned as eligible to this role)
        $_Notification_Admin_Admin_Eligibility = $response.properties.rules | Where-Object { $_.id -eq "Notification_Admin_Admin_Eligibility" }
        # Notification Eligibility Assignee (Send notifications when members are assigned as eligible to this role: Notification to the assigned user (assignee))
        $_Notification_Eligibility_Assignee = $response.properties.rules | Where-Object { $_.id -eq "Notification_Requestor_Admin_Eligibility" }
        # Notification Eligibility Approvers (Send notifications when members are assigned as eligible to this role: request to approve a role assignment renewal/extension)
        $_Notification_Eligibility_Approvers = $response.properties.rules | Where-Object { $_.id -eq "Notification_Approver_Admin_Eligibility" }

        # Notification Active Assignment Alert (Send notifications when members are assigned as active to this role)
        $_Notification_Active_Alert = $response.properties.rules | Where-Object { $_.id -eq "Notification_Admin_Admin_Assignment" }
        # Notification Active Assignment Assignee (Send notifications when members are assigned as active to this role: Notification to the assigned user (assignee))
        $_Notification_Active_Assignee = $response.properties.rules | Where-Object { $_.id -eq "Notification_Requestor_Admin_Assignment" }
        # Notification Active Assignment Approvers (Send notifications when members are assigned as active to this role: Request to approve a role assignment renewal/extension)
        $_Notification_Active_Approvers = $response.properties.rules | Where-Object { $_.id -eq "Notification_Approver_Admin_Assignment" }
        
        # Notification Role Activation Alert (Send notifications when eligible members activate this role: Role activation alert)
        $_Notification_Activation_Alert = $response.properties.rules | Where-Object { $_.id -eq "Notification_Admin_EndUser_Assignment" }
        # Notification Role Activation Assignee (Send notifications when eligible members activate this role: Notification to activated user (requestor))
        $_Notification_Activation_Assignee = $response.properties.rules | Where-Object { $_.id -eq "Notification_Requestor_EndUser_Assignment" }
        # Notification Role Activation Approvers (Send notifications when eligible members activate this role: Request to approve an activation)
        $_Notification_Activation_Approver = $response.properties.rules | Where-Object { $_.id -eq "Notification_Approver_EndUser_Assignment" }


        $config = [PSCustomObject]@{
            RoleName                                                     = $_
            PolicyID                                                     = $policyId
            ActivationDuration                                           = $_activationDuration
            EnablementRules                                              = $_enablementRules -join ','
            ApprovalRequired                                             = $_approvalrequired
            Approvers                                                    = $_approvers -join ','
            AllowPermanentEligibleAssignment                             = $_permanantEligibility
            MaximumEligibleAssignmentDuration                            = $_maxAssignmentDuration
            AllowPermanentActiveAssignment                               = $_permanantActiveAssignment
            MaximumActiveAssignmentDuration                              = $_maxActiveAssignmentDuration
            Notification_Eligibility_Alert_isDefaultRecipientEnabled     = $($_Notification_Admin_Admin_Eligibility.isDefaultRecipientsEnabled)
            Notification_Eligibility_Alert_NotificationLevel             = $($_Notification_Admin_Admin_Eligibility.notificationLevel)
            Notification_Eligibility_Alert_Recipients                    = $($_Notification_Admin_Admin_Eligibility.notificationRecipients) -join ','
            Notification_Eligibility_Assignee_isDefaultRecipientEnabled  = $($_Notification_Eligibility_Assignee.isDefaultRecipientsEnabled)
            Notification_Eligibility_Assignee_NotificationLevel          = $($_Notification_Eligibility_Assignee.NotificationLevel)
            Notification_Eligibility_Assignee_Recipients                 = $($_Notification_Eligibility_Assignee.notificationRecipients) -join ','
            Notification_Eligibility_Approvers_isDefaultRecipientEnabled = $($_Notification_Eligibility_Approvers.isDefaultRecipientsEnabled)
            Notification_Eligibility_Approvers_NotificationLevel         = $($_Notification_Eligibility_Approvers.NotificationLevel)
            Notification_Eligibility_Approvers_Recipients                = $($_Notification_Eligibility_Approvers.notificationRecipients -join ',')
            Notification_Active_Alert_isDefaultRecipientEnabled          = $($_Notification_Active_Alert.isDefaultRecipientsEnabled)
            Notification_Active_Alert_NotificationLevel                  = $($_Notification_Active_Alert.notificationLevel)
            Notification_Active_Alert_Recipients                         = $($_Notification_Active_Alert.notificationRecipients -join ',')
            Notification_Active_Assignee_isDefaultRecipientEnabled       = $($_Notification_Active_Assignee.isDefaultRecipientsEnabled)
            Notification_Active_Assignee_NotificationLevel               = $($_Notification_Active_Assignee.notificationLevel)
            Notification_Active_Assignee_Recipients                      = $($_Notification_Active_Assignee.notificationRecipients -join ',')
            Notification_Active_Approvers_isDefaultRecipientEnabled      = $($_Notification_Active_Approvers.isDefaultRecipientsEnabled)
            Notification_Active_Approvers_NotificationLevel              = $($_Notification_Active_Approvers.notificationLevel)
            Notification_Active_Approvers_Recipients                     = $($_Notification_Active_Approvers.notificationRecipients -join ',')
            Notification_Activation_Alert_isDefaultRecipientEnabled      = $($_Notification_Activation_Alert.isDefaultRecipientsEnabled)
            Notification_Activation_Alert_NotificationLevel              = $($_Notification_Activation_Alert.NotificationLevel)
            Notification_Activation_Alert_Recipients                     = $($_Notification_Activation_Alert.NotificationRecipients -join ',')
            Notification_Activation_Assignee_isDefaultRecipientEnabled   = $($_Notification_Activation_Assignee.isDefaultRecipientsEnabled)
            Notification_Activation_Assignee_NotificationLevel           = $($_Notification_Activation_Assignee.NotificationLevel)
            Notification_Activation_Assignee_Recipients                  = $($_Notification_Activation_Assignee.NotificationRecipients -join ',')
            Notification_Activation_Approver_isDefaultRecipientEnabled  = $($_Notification_Activation_Approver.isDefaultRecipientsEnabled)
            Notification_Activation_Approver_NotificationLevel          = $($_Notification_Activation_Approver.NotificationLevel)
            Notification_Activation_Approver_Recipients                 = $($_Notification_Activation_Approver.NotificationRecipients -join ',')
        }
        return $config
    }
    catch {
        Mycatch $_
    }
}

<#
    .Synopsis
        Import the settings from the csv file $path
    .Description
        Convert the csv back to policy rules
    .Parameter Path
        path to the csv file
    .Example
        PS> Import-Setting -path "c:\temp\myrole.csv"
 
        Import settings from file c:\temp\myrole.csv
      
    .Notes
        Author: Loïc MICHEL
        Homepage: https://github.com/kayasax/EasyPIM
     #>

function Import-Setting ($path) {
    log "Importing setting from $path"
    if (!(test-path $path)) {
        throw "Operation failed, file $path cannot be found"
    }
    $csv = Import-Csv $path

    $csv | ForEach-Object {
        $rules = @()
        $rules += Set-ActivationDuration $_.ActivationDuration
        $enablementRules = $_.EnablementRules.Split(',')
        $rules += Set-ActivationRequirement $enablementRules
        $approvers = @()
        $approvers += $_.approvers
        $rules += Set-ApprovalFromCSV $_.ApprovalRequired $Approvers
        $rules += Set-EligibilityAssignmentFromCSV $_.MaximumEligibleAssignmentDuration $_.AllowPermanentEligibleAssignment
        $rules += Set-ActiveAssignmentFromCSV $_.MaximumActiveAssignmentDuration $_.AllowPermanentActiveAssignment
            
        $Notification_EligibleAssignment_Alert = @{
            "isDefaultRecipientEnabled" = $_.Notification_Eligibility_Alert_isDefaultRecipientEnabled;
            "notificationLevel"         = $_.Notification_Eligibility_Alert_notificationLevel;
            "Recipients"                = $_.Notification_Eligibility_Alert_Recipients.split(',')
        }
        $rules += Set-Notification_EligibleAssignment_Alert $Notification_EligibleAssignment_Alert

        $Notification_EligibleAssignment_Assignee = @{
            "isDefaultRecipientEnabled" = $_.Notification_Eligibility_Assignee_isDefaultRecipientEnabled;
            "notificationLevel"         = $_.Notification_Eligibility_Assignee_notificationLevel;
            "Recipients"                = $_.Notification_Eligibility_Assignee_Recipients.split(',')
        }
        $rules += Set-Notification_EligibleAssignment_Assignee $Notification_EligibleAssignment_Assignee
            
        $Notification_EligibleAssignment_Approver = @{
            "isDefaultRecipientEnabled" = $_.Notification_Eligibility_Approvers_isDefaultRecipientEnabled;
            "notificationLevel"         = $_.Notification_Eligibility_Approvers_notificationLevel;
            "Recipients"                = $_.Notification_Eligibility_Approvers_Recipients.split(',')
        }
        $rules += Set-Notification_EligibleAssignment_Approver $Notification_EligibleAssignment_Approver

        $Notification_Active_Alert = @{
            "isDefaultRecipientEnabled" = $_.Notification_Active_Alert_isDefaultRecipientEnabled;
            "notificationLevel"         = $_.Notification_Active_Alert_notificationLevel;
            "Recipients"                = $_.Notification_Active_Alert_Recipients.split(',')
        }
        $rules += Set-Notification_ActiveAssignment_Alert $Notification_Active_Alert
            
        $Notification_Active_Assignee = @{
            "isDefaultRecipientEnabled" = $_.Notification_Active_Assignee_isDefaultRecipientEnabled;
            "notificationLevel"         = $_.Notification_Active_Assignee_notificationLevel;
            "Recipients"                = $_.Notification_Active_Assignee_Recipients.split(',')
        }
        $rules += Set-Notification_ActiveAssignment_Assignee $Notification_Active_Assignee
            
        $Notification_Active_Approvers = @{
            "isDefaultRecipientEnabled" = $_.Notification_Active_Approvers_isDefaultRecipientEnabled;
            "notificationLevel"         = $_.Notification_Active_Approvers_notificationLevel;
            "Recipients"                = $_.Notification_Active_Approvers_Recipients.split(',')
        }
        $rules += Set-Notification_ActiveAssignment_Approver $Notification_Active_Approvers

        $Notification_Activation_Alert = @{
            "isDefaultRecipientEnabled" = $_.Notification_Activation_Alert_isDefaultRecipientEnabled;
            "notificationLevel"         = $_.Notification_Activation_Alert_notificationLevel;
            "Recipients"                = $_.Notification_Activation_Alert_Recipients.split(',')
        }
        $rules += Set-Notification_Activation_Alert $Notification_Activation_Alert

        $Notification_Activation_Assignee = @{
            "isDefaultRecipientEnabled" = $_.Notification_Activation_Assignee_isDefaultRecipientEnabled;
            "notificationLevel"         = $_.Notification_Activation_Assignee_notificationLevel;
            "Recipients"                = $_.Notification_Activation_Assignee_Recipients.split(',')
        }
        $rules += Set-Notification_Activation_Assignee $Notification_Activation_Assignee

        $Notification_Activation_Approver = @{
            "isDefaultRecipientEnabled" = $_.Notification_Activation_Approver_isDefaultRecipientEnabled;
            "notificationLevel"         = $_.Notification_Activation_Approver_notificationLevel;
            "Recipients"                = $_.Notification_Activation_Approver_Recipients.split(',')
        }
        $rules += Set-Notification_Activation_Approver $Notification_Activation_Approver
            
        # patch the policy
        Update-Policy $_.policyID $($rules -join ',')
    }
}


<#
      .Synopsis
       invoke ARM REST API
      .Description
       wrapper function to get an access token and set authentication header for each ARM API call
      .Parameter RestURI
       the URI
      .Parameter Method
       http method to use
      .Parameter Body
       an optional body
      .Example
        PS> invoke-ARM -restURI $restURI -method "GET"
 
        will send an GET query to $restURI and return the response
      .Link
      
      .Notes
        Author: Loïc MICHEL
        Homepage: https://github.com/kayasax/EasyPIM
#>

function Invoke-ARM {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $restURI,

        [Parameter(Position = 1)]
        [System.String]
        $method,

        [Parameter(Position = 2)]
        [System.String]
        $body=""
    )

    try{
        <#$scope = "subscriptions/$script:subscriptionID"
        $ARMhost = "https://management.azure.com"
        $ARMendpoint = "$ARMhost/$scope/providers/Microsoft.Authorization"#>


        write-verbose "`n>> request body: $body"
        write-verbose "request URI : $restURI"


        if ( $null -eq (get-azcontext) -or ( (get-azcontext).Tenant.Id -ne $script:tenantID ) ) {
            Write-Verbose ">> Connecting to Azure with tenantID $script:tenantID"
            Connect-AzAccount -Tenant $script:tenantID
        }
    
        # Get access Token
        Write-Verbose ">> Getting access token"
        $token = Get-AzAccessToken
                
        # setting the authentication headers for MSGraph calls
        $authHeader = @{
            'Content-Type'  = 'application/json'
            'Authorization' = 'Bearer ' + $token.Token
        }

        $response = Invoke-RestMethod -Uri $restUri -Method $method -Headers $authHeader -Body $body -verbose:$false
        return $response

    }
    catch{
        MyCatch $_
    }


}

<#
      .Synopsis
       Log message to file and display it on screen with basic colour hilighting.
       The function include a log rotate feature.
      .Description
       Write $msg to screen and file with additional inforamtions : date and time,
       name of the script from where the function was called, line number and user who ran the script.
       If logfile path isn't specified it will default to C:\UPF\LOGS\<scriptname.ps1.log>
       You can use $Maxsize and $MaxFile to specified the size and number of logfiles to keep (default is 3MB, and 3files)
       Use the switch $noEcho if you dont want the message be displayed on screen
      .Parameter msg
       The message to log
      .Parameter logfile
       Name of the logfile to use (default = <scriptname>.ps1.log)
      .Parameter logdir
       Path to the logfile's directory (defaut = <scriptpath>\LOGS)
       .Parameter noEcho
       Don't print message on screen
      .Parameter maxSize
       Maximum size (in bytes) before logfile is rotate (default is 3MB)
      .Parameter maxFile
       Number of logfile history to keep (default is 3)
      .EXAMPLE
        PS> log "A message to display on screen and file"
 
        message will be dispayed and saved to file
 
      .Example
        PS> log "this message will not appear on screen" -noEcho
 
        this message will be log to the file without any display
      .Link
      
      .Notes
          Changelog :
         * 27/08/2017 version initiale
         * 21/09/2017 correction of rotating step
          Todo :
     #>

function log {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
    [CmdletBinding()]
    param(
        [string]$msg,
        $logfile = $null,
        $logdir = $(join-path -path $script:_logPath -childpath "LOGS"), # Path to logfile
        [switch] $noEcho, # if set dont display output to screen, only to logfile
        $MaxSize = 3145728, # 3MB
        #$MaxSize = 1,
        $Maxfile = 3 # how many files to keep
    )

    write-verbose "noecho? $noecho"
    #do nothing if logging is disabled
    if ($true -eq $script:logToFile ) {
     
        # When no logfile is specified we append .log to the scriptname
        if ( $null -eq $logfile ) {
            $logfile = "EasyPIM.log"
        }
       
        # Create folder if needed
        if ( !(test-path  $logdir) ) {
            $null = New-Item -ItemType Directory -Path $logdir  -Force
        }
         
        # Ensure logfile will be save in logdir
        if ( $logfile -notmatch [regex]::escape($logdir)) {
            $logfile = "$logdir\$logfile"
        }
         
        # Create file
        if ( !(Test-Path $logfile) ) {
            write-verbose "$logfile not found, creating it"
            $null = New-Item -ItemType file $logfile -Force
        }
        else {
            # file exists, do size exceeds limit ?
            if ( (get-childitem $logfile | Select-Object -expand length) -gt $Maxsize) {
                write-host "$(Get-Date -Format yyyy-MM-dd HH:mm) - $(whoami) - $($MyInvocation.ScriptName) (L $($MyInvocation.ScriptLineNumber)) : Log size exceed $MaxSize, creating a new file." >> $logfile
                 
                # rename current logfile
                $LogFileName = $($($LogFile -split "\\")[-1])
                $basename = Get-ChildItem $LogFile | Select-Object -expand basename
                $dirname = Get-ChildItem $LogFile | Select-Object -expand directoryname
     
                Write-Verbose "Rename-Item $LogFile ""$($LogFileName.substring(0,$LogFileName.length-4))-$(Get-Date -format yyyddMM-HHmmss).log"""
                Rename-Item $LogFile "$($LogFileName.substring(0,$LogFileName.length-4))-$(Get-Date -format yyyddMM-HHmmss).log"
     
                # keep $Maxfile logfiles and delete the older ones
                $filesToDelete = Get-ChildItem  "$dirname\$basename*.log" | Sort-Object LastWriteTime -desc | Select-Object -Skip $Maxfile
                $filesToDelete | remove-item  -force
            }
        }
     
        write-output "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - $(whoami) - $($MyInvocation.ScriptName) (L $($MyInvocation.ScriptLineNumber)) : $msg" >> $logfile
    }# end logging to file

    # Display $msg if $noEcho is not set
    if ( $noEcho -eq $false) {
        #colour it up...
        if ( $msg -match "Erreur|error") {
            write-host $msg -ForegroundColor red
        }
        elseif ($msg -match "avertissement|attention|warning") {
            write-host $msg -ForegroundColor yellow
        }
        elseif ($msg -match "info|information") {
            write-host $msg -ForegroundColor cyan
        }
        elseif ($msg -match "succès|succes|success|OK") {
            write-host $msg -ForegroundColor green
        }
        else {
            write-host $msg
        }
    }

    
}


<#
      .Synopsis
       wrapper for all caught exceptions
      .Description
       the exception will be parsed to get the details, it will be logged and eventualy sent to Teams if the notification is enabled
      .Parameter e
       The exception that was sent
      .EXAMPLE
        PS> MyCatch $e
 
        Will log the details of the exception
       
      .Link
      
      .Notes
           
#>

     function MyCatch($e){
    $err = $($e.exception.message | out-string)
    $details =$e.errordetails# |fl -force
    $position = $e.InvocationInfo.positionMessage
    #$Exception = $e.Exception
    
    if ($TeamsNotif) { send-teamsnotif "$err" "$details<BR/> TIPS: try to check the scope and the role name" "$position" }
    Log "An exception occured: $err `nDetails: $details `nPosition: $position"
    Log "Error, script did not terminate normaly"
    break
}

<#
      .Synopsis
       Send message to Teams channel
      .Description
       The app "inbound webhook" must be configured for that channed and the url set in scripts/variables.ps1
      .Parameter message
       message to display
      .Parameter details
       placeholder for more details
      .Parameter myStackTrace
       place holder for stack trace
      .Example
       PS> send-teamsnotif "Error occured" "The source file was not found"
 
       Send a notification to teams webhook url
      
      .Notes
#>
function send-teamsnotif {
    [CmdletBinding()] #make script react as cmdlet (-verbose etc..)
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $message,
        [string] $details,
        [string] $myStackTrace = $null
    )

    $JSONBody = @{
        "@type"    = "MessageCard"
        "@context" = "<http://schema.org/extensions>"
        "title"    = "Alert for $description @ $env:computername "
        "text"     = "An exception occured:"
        "sections" = @(
            @{
                "activityTitle" = "Message : $message"
            },
            @{
                "activityTitle" = "Details : $details"
            },
            @{
                "activityTitle" = " Script path "
                "activityText"  = "$_scriptFullName"
            },
            
            @{
                "activityTitle" = "myStackTrace"
                "activityText"  = "$myStackTrace"
            }
        )
    }

    $TeamMessageBody = ConvertTo-Json $JSONBody -Depth 100
        
    $parameters = @{
        "URI"         = $teamsWebhookURL
        "Method"      = 'POST'
        "Body"        = $TeamMessageBody
        "ContentType" = 'application/json'
    }
    $null = Invoke-RestMethod @parameters
}


<#
      .Synopsis
       Rule for maximum activation duration
      .Description
       rule 1 in https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#activation-rules
      .Parameter ActivationDuration
       Maximum activation duration. Duration ref: https://en.wikipedia.org/wiki/ISO_8601#Durations
      .EXAMPLE
        PS> Set-ActivationDuration "PT8H"
 
        limit the activation duration to 8 hours
       
      .Link
      
      .Notes
           
#>

function Set-ActivationDuration ($ActivationDuration) {
    # Set Maximum activation duration
    if ( ($null -ne $ActivationDuration) -and ("" -ne $ActivationDuration) ) {
        Write-Verbose "Editing Activation duration : $ActivationDuration"
        $properties = @{
            "isExpirationRequired" = "true";
            "maximumDuration"      = "$ActivationDuration";
            "id"                   = "Expiration_EndUser_Assignment";
            "ruleType"             = "RoleManagementPolicyExpirationRule";
            "target"               = @{
                "caller"     = "EndUser";
                "operations" = @("All")
            };
            "level"                = "Assignment"
        }
        $rule = $properties | ConvertTo-Json
        #update rules if required
        return $rule
    }
}


<#
      .Synopsis
       Rule for activation requirement
      .Description
       rule 2 in https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#activation-rules
      .Parameter ActivationRequirement
       value can be "None", or one or more value from "Justification","Ticketing","MultiFactoAuthentication"
       WARNING options are case sensitive!
      .EXAMPLE
        PS> Set-Activationrequirement "Justification"
 
        A justification will be required to activate the role
       
      .Link
      
      .Notes
           
#>

function Set-ActivationRequirement($ActivationRequirement) {
    write-verbose "Set-ActivationRequirement : $($ActivationRequirement.length)"
    if (($ActivationRequirement -eq "None") -or ($ActivationRequirement[0].length -eq 0 )) {
        #if none or a null array
        write-verbose "requirement is nul"
        $enabledRules = "[],"
    }
    else {
        write-verbose "requirement is NOT nul"
        $formatedRules = '['
            
        $ActivationRequirement | ForEach-Object {
            $formatedRules += '"'
            $formatedRules += "$_"
            $formatedRules += '",'
        }
        #remove last comma
        $formatedRules = $formatedRules -replace “.$”

        $formatedRules += "],"
        $enabledRules = $formatedRules
        #Write-Verbose "************* $enabledRules "
    }
            
    $properties = '{
                "enabledRules": '
+ $enabledRules + '
                "id": "Enablement_EndUser_Assignment",
                "ruleType": "RoleManagementPolicyEnablementRule",
                "target": {
                    "caller": "EndUser",
                    "operations": [
                        "All"
                    ],
                    "level": "Assignment",
                    "targetObjects": [],
                    "inheritableSettings": [],
                    "enforcedSettings": []
                }
            }'


    return $properties
}


<#
      .Synopsis
       Rule for maximum active assignment
      .Description
       rule 6 in https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#assignment-rules
      .Parameter MaximumActiveAssignmentDuration
       Maximum active assignment duration. Duration ref: https://en.wikipedia.org/wiki/ISO_8601#Durations
      .Parameter AllowPermanentActiveAssignment
        Allow permanent active assignement ?
      .EXAMPLE
        PS> Set-ActiveAssignment -MaximumActiveAssignmentDuration "P30D" -AllowPermanentActiveAssignment $false
 
        limit the active assignment duration to 30 days
       
      .Link
      
      .Notes
           
#>

function Set-ActiveAssignment($MaximumActiveAssignmentDuration, $AllowPermanentActiveAssignment) {
    write-verbose "Set-ActiveAssignment($MaximumActiveAssignmentDuration, $AllowPermanentActiveAssignment)"
    if ( $true -eq 'AllowPermanentActiveAssignment') {
        $expire2 = "false"
    }
    else {
        $expire2 = "true"
    }
            
    $rule = '
        {
        "isExpirationRequired": '
+ $expire2 + ',
        "maximumDuration": "'
+ $MaximumActiveAssignmentDuration + '",
        "id": "Expiration_Admin_Assignment",
        "ruleType": "RoleManagementPolicyExpirationRule",
        "target": {
        "caller": "Admin",
        "operations": [
            "All"
        ],
        "level": "Eligibility",
        "targetObjects": null,
        "inheritableSettings": null,
        "enforcedSettings": null
        }
        }
    '

    return $rule
        
}


<#
      .Synopsis
       Rule for maximum active assignment
      .Description
       rule 6 in https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#assignment-rules
      .Parameter MaximumActiveAssignmentDuration
       Maximum active assignment duration. Duration ref: https://en.wikipedia.org/wiki/ISO_8601#Durations
      .Parameter AllowPermanentActiveAssignment
        Allow permanent active assignement ?
      .EXAMPLE
        PS> Set-ActiveAssignment -MaximumActiveAssignmentDuration "P30D" -AllowPermanentActiveAssignment $false
 
        limit the active assignment duration to 30 days
       
      .Link
      
      .Notes
           
#>

function Set-ActiveAssignmentFromCSV($MaximumActiveAssignmentDuration, $AllowPermanentActiveAssignment) {
    write-verbose "Set-ActiveAssignmentFromCSV($MaximumActiveAssignmentDuration, $AllowPermanentActiveAssignment)"
    if ( "true" -eq $AllowPermanentActiveAssignment) {
        $expire2 = "false"
    }
    else {
        $expire2 = "true"
    }
            
    $rule = '
        {
        "isExpirationRequired": '
+ $expire2 + ',
        "maximumDuration": "'
+ $MaximumActiveAssignmentDuration + '",
        "id": "Expiration_Admin_Assignment",
        "ruleType": "RoleManagementPolicyExpirationRule",
        "target": {
        "caller": "Admin",
        "operations": [
            "All"
        ],
        "level": "Eligibility",
        "targetObjects": null,
        "inheritableSettings": null,
        "enforcedSettings": null
        }
        }
    '

    return $rule
        
}


<#
      .Synopsis
       Define if approval is required to activate a role, and who are the approvers
      .Description
       rule 4 in https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#activation-rules
      .Parameter ApprovalRequired
       Do we need an approval to activate a role?
      .Parameter Approvers
        Who is the approver?
      .EXAMPLE
        PS> Set-Approval -ApprovalRequired $true -Approvers @(@{"Id"=$UID;"Name"="John":"Type"="user"}, @{"Id"=$GID;"Name"="Group1":"Type"="group"})
 
        define John and Group1 as approvers and require approval
       
      .Link
      
      .Notes
           
#>

function Set-Approval ($ApprovalRequired, $Approvers) {
    Write-Verbose "Set-Approval"
    if ($null -eq $Approvers) { $Approvers = $config.Approvers }
    if ($ApprovalRequired -eq $false) { $req = "false" }else { $req = "true" }
        
    $rule = '
        {
        "setting": {'

    if ($null -ne $ApprovalRequired) {
        $rule += '"isApprovalRequired": ' + $req + ','
    }
    $rule += '
        "isApprovalRequiredForExtension": false,
        "isRequestorJustificationRequired": true,
        "approvalMode": "SingleStage",
        "approvalStages": [
            {
            "approvalStageTimeOutInDays": 1,
            "isApproverJustificationRequired": true,
            "escalationTimeInMinutes": 0,
        '


    if ($null -ne $Approvers) {
        #at least one approver required if approval is enable
        $rule += '
            "primaryApprovers": [
            '

        $cpt = 0
        $Approvers | ForEach-Object {
            #write-host $_
            $id = $_.Id
            $name = $_.Name
            $type = $_.Type

            if ($cpt -gt 0) {
                $rule += ","
            }
            $rule += '
            {
                "id": "'
+ $id + '",
                "description": "'
+ $name + '",
                "isBackup": false,
                "userType": "'
+ $type + '"
            }
            '

            $cpt++
        }

        $rule += '
            ],'

    }

    $rule += '
        "isEscalationEnabled": false,
            "escalationApprovers": null
                    }]
                 },
        "id": "Approval_EndUser_Assignment",
        "ruleType": "RoleManagementPolicyApprovalRule",
        "target": {
            "caller": "EndUser",
            "operations": [
                "All"
            ],
            "level": "Assignment",
            "targetObjects": null,
            "inheritableSettings": null,
            "enforcedSettings": null
         
        }}'

    return $rule
}


<#
      .Synopsis
       Define if approval is required to activate a role, and who are the approvers
      .Description
       rule 4 in https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#activation-rules
      .Parameter ApprovalRequired
       Do we need an approval to activate a role?
      .Parameter Approvers
        Who is the approver?
      .EXAMPLE
        PS> Set-Approval -ApprovalRequired $true -Approvers @(@{"Id"=$UID;"Name"="John":"Type"="user"}, @{"Id"=$GID;"Name"="Group1":"Type"="group"})
 
        define John and Group1 as approvers and require approval
       
      .Link
      
      .Notes
           
#>

function Set-ApprovalFromCSV ($ApprovalRequired, $Approvers) {
    write-verbose "Set-ApprovalFromCSV"
    if ($null -eq $Approvers) { $Approvers = $config.Approvers }
    if ($ApprovalRequired -eq $false) { $req = "false" }else { $req = "true" }
        
    $rule = '
        {
        "setting": {'

    if ($null -ne $ApprovalRequired) {
        $rule += '"isApprovalRequired":' + $req + ','
    }
       
    $rule += '
        "isApprovalRequiredForExtension": false,
        "isRequestorJustificationRequired": true,
        "approvalMode": "SingleStage",
        "approvalStages": [
            {
            "approvalStageTimeOutInDays": 1,
            "isApproverJustificationRequired": true,
            "escalationTimeInMinutes": 0,
        '


    if ($null -ne $Approvers) {
        #at least one approver required if approval is enable

        $Approvers = $Approvers -replace "@"
        $Approvers = $Approvers -replace ";", ","
        $Approvers = $Approvers -replace "=", ":"

        $rule += '
            "primaryApprovers": [
            '
+ $Approvers
    }

    $rule += '
            ],'

        

    $rule += '
        "isEscalationEnabled": false,
            "escalationApprovers": null
                    }]
                 },
        "id": "Approval_EndUser_Assignment",
        "ruleType": "RoleManagementPolicyApprovalRule",
        "target": {
            "caller": "EndUser",
            "operations": [
                "All"
            ],
            "level": "Assignment",
            "targetObjects": null,
            "inheritableSettings": null,
            "enforcedSettings": null
         
        }}'

    return $rule
}


<#
      .Synopsis
       definne the eligible assignment setting : max duration and if permanent eligibility is allowed
      .Description
       correspond to rule 5 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#assignment-rules
      .Parameter MaximumEligibilityDuration
       maximum duration of an eligibility
      .Parameter AllowPermanentEligibility
       Do we allow permanent eligibility
      .Example
       PS> Set-EligibilityAssignment -MaximumEligibilityDuration "P30D" -AllowPermanentEligibility $false
 
       set Max eligibility duration to 30 days
      .Link
      
      .Notes
#>

function Set-EligibilityAssignment($MaximumEligibilityDuration, $AllowPermanentEligibility) {
    write-verbose "Set-EligibilityAssignment: $MaximumEligibilityDuration $AllowPermanentEligibility"
    $max = $MaximumEligibilityDuration
     
    if ( ($true -eq $AllowPermanentEligibility) -or ("true" -eq $AllowPermanentEligibility) -and ("false" -ne $AllowPermanentEligibility)) {
        $expire = "false"
        write-verbose "1 setting expire to : $expire"
    }
    else {
            
        $expire = "true"
        write-verbose "2 setting expire to : $expire"
    }
      
    $rule = '
        {
        "isExpirationRequired": '
+ $expire + ',
        "maximumDuration": "'
+ $max + '",
        "id": "Expiration_Admin_Eligibility",
        "ruleType": "RoleManagementPolicyExpirationRule",
        "target": {
          "caller": "Admin",
          "operations": [
            "All"
          ],
          "level": "Eligibility",
          "targetObjects": null,
          "inheritableSettings": null,
          "enforcedSettings": null
        }
    }
    '

    # update rule only if a change was requested
    return $rule
}


<#
      .Synopsis
       definne the eligible assignment setting : max duration and if permanent eligibility is allowed
      .Description
       correspond to rule 5 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#assignment-rules
      .Parameter MaximumEligibilityDuration
       maximum duration of an eligibility
      .Parameter AllowPermanentEligibility
       Do we allow permanent eligibility
      .EXAMPLE
        PS> Set-EligibilityAssignment -MaximumEligibilityDuration "P30D" -AllowPermanentEligibility $false
 
        define a maximum eligibility duration of 30 days
      .Link
      
      .Notes
#>

function Set-EligibilityAssignmentFromCSV($MaximumEligibilityDuration, $AllowPermanentEligibility) {
    write-verbose "Set-EligibilityAssignmentFromCSV: $MaximumEligibilityDuration $AllowPermanentEligibility"
    $max = $MaximumEligibilityDuration
     
    if ( "true" -eq $AllowPermanentEligibility) {
        $expire = "false"
        write-verbose "1 setting expire to : $expire"
    }
    else {
            
        $expire = "true"
        write-verbose "2 setting expire to : $expire"
    }
      
    $rule = '
        {
        "isExpirationRequired": '
+ $expire + ',
        "maximumDuration": "'
+ $max + '",
        "id": "Expiration_Admin_Eligibility",
        "ruleType": "RoleManagementPolicyExpirationRule",
        "target": {
          "caller": "Admin",
          "operations": [
            "All"
          ],
          "level": "Eligibility",
          "targetObjects": null,
          "inheritableSettings": null,
          "enforcedSettings": null
        }
    }
    '

    # update rule only if a change was requested
    return $rule
}


<#
.Synopsis
Admin notification when a role is activated
.Description
notification setting corresponding to rule 15 here https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#notification-rules
.Parameter Notification_Activation_Alert
hashtable for the settings like: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
.Example
PS> Set-Notification_Activation_Alert -Notification_Activation_Alert @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
set the notification sent to Admins when a role is activated
#>

function Set-Notification_Activation_Alert($Notification_Activation_Alert) {
    $rule = '
        {
        "notificationType": "Email",
        "recipientType": "Admin",
        "isDefaultRecipientsEnabled": '
+ $Notification_Activation_Alert.isDefaultRecipientEnabled.ToLower() + ',
        "notificationLevel": "'
+ $Notification_Activation_Alert.notificationLevel + '",
        "notificationRecipients": [
        '

    $Notification_Activation_Alert.Recipients | ForEach-Object {
        $rule += '"' + $_ + '",'
    }

    $rule += '
        ],
        "id": "Notification_Admin_EndUser_Assignment",
        "ruleType": "RoleManagementPolicyNotificationRule",
        "target": {
        "caller": "Admin",
        "operations": [
            "All"
        ],
        "level": "Eligibility",
        "targetObjects": null,
        "inheritableSettings": null,
        "enforcedSettings": null
        }
        }
        '

    return $rule
}


<#
      .Synopsis
       Approver notification when a role is activated
      .Description
       correspond to rule 1 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#notification-rules
      .Parameter Notification_Activation_Approver
      hashtable for the settings like: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
       
      .Example
       PS> Set-Notification_Activation_Alert -Notification_Activation_Alert @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
       set the notification sent to Admins when a role is activated
      .Link
      
      .Notes
#>

function Set-Notification_Activation_Approver ($Notification_Activation_Approver) {
    $rule = '
        {
        "notificationType": "Email",
        "recipientType": "Approver",
        "isDefaultRecipientsEnabled": '
+ $Notification_Activation_Approver.isDefaultRecipientEnabled.ToLower() + ',
        "notificationLevel": "'
+ $Notification_Activation_Approver.notificationLevel + '",
        "notificationRecipients": [
        '

    <#
            # Cant add backup recipient for this rule
 
            $Notification_Activation_Approver.Recipients | % {
                $rule += '"' + $_ + '",'
            }
        #>

    $rule += '
        ],
        "id": "Notification_Approver_EndUser_Assignment",
        "ruleType": "RoleManagementPolicyNotificationRule",
        "target": {
        "caller": "Admin",
        "operations": [
            "All"
        ],
        "level": "Eligibility",
        "targetObjects": null,
        "inheritableSettings": null,
        "enforcedSettings": null
        }
        }
        '

    return $rule
}


<#
.Synopsis
Assignee notification when a role is activated
.Description
correspond to rule 16 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#notification-rules
.Parameter Notification_Activation_Assignee
hashtable for the settings like: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
.Example
PS> Set-Notification_Activation_Assignee -Notification_Activation_Assignee @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
set the notification sent to assignee when a role is activated
.Link
 
.Notes
#>

function set-Notification_Activation_Assignee($Notification_Activation_Assignee) {
    $rule = '
         {
         "notificationType": "Email",
         "recipientType": "Requestor",
         "isDefaultRecipientsEnabled": '
+ $Notification_Activation_Assignee.isDefaultRecipientEnabled.ToLower() + ',
         "notificationLevel": "'
+ $Notification_Activation_Assignee.notificationLevel + '",
         "notificationRecipients": [
         '

    $Notification_Activation_Assignee.Recipients | ForEach-Object {
        $rule += '"' + $_ + '",'
    }
 
    $rule += '
         ],
         "id": "Notification_Requestor_EndUser_Assignment",
         "ruleType": "RoleManagementPolicyNotificationRule",
         "target": {
         "caller": "Admin",
         "operations": [
             "All"
         ],
         "level": "Eligibility",
         "targetObjects": null,
         "inheritableSettings": null,
         "enforcedSettings": null
         }
         }
         '

    return $rule
}


<#
.Synopsis
admin notification when an active assignment is created
.Description
correspond to rule 12 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#notification-rules
.Parameter Notification_ActiveAssignment_Alert
hashtable for the settings like: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
.Example
PS> Set-Notification_ActiveAssignment_Alert -Notification_ActiveAssignment_Alert @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
set the notification sent to admin when active assignment is created
.Link
 
.Notes
#>

function Set-Notification_ActiveAssignment_Alert($Notification_ActiveAssignment_Alert) {
    $rule = '
    {
    "notificationType": "Email",
    "recipientType": "Admin",
    "isDefaultRecipientsEnabled": '
+ $Notification_ActiveAssignment_Alert.isDefaultRecipientEnabled.ToLower() + ',
    "notificationLevel": "'
+ $Notification_ActiveAssignment_Alert.notificationLevel + '",
    "notificationRecipients": [
    '

    $Notification_ActiveAssignment_Alert.Recipients | ForEach-Object {
        $rule += '"' + $_ + '",'
    }
    
    $rule += '
    ],
    "id": "Notification_Admin_Admin_Assignment",
    "ruleType": "RoleManagementPolicyNotificationRule",
    "target": {
    "caller": "Admin",
    "operations": [
        "All"
    ],
    "level": "Eligibility",
    "targetObjects": null,
    "inheritableSettings": null,
    "enforcedSettings": null
    }
    }
    '

    return $rule
}


<#
.Synopsis
approver notification when an active assignment is created
.Description
correspond to rule 14 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#notification-rules
.Parameter Notification_ActiveAssignment_Approver
hashtable for the settings like: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
.Example
PS> Set-Notification_ActiveAssignment_Approver -Notification_ActiveAssignment_Approver @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
set the notification sent to approvers when active assignment is created
.Link
 
.Notes
#>

function  Set-Notification_ActiveAssignment_Approver($Notification_ActiveAssignment_Approver) {
    $rule = '
        {
        "notificationType": "Email",
        "recipientType": "Approver",
        "isDefaultRecipientsEnabled": '
+ $Notification_ActiveAssignment_Approver.isDefaultRecipientEnabled.ToLower() + ',
        "notificationLevel": "'
+ $Notification_ActiveAssignment_Approver.notificationLevel + '",
        "notificationRecipients": [
        '

    $Notification_ActiveAssignment_Approver.Recipients | ForEach-Object {
        $rule += '"' + $_ + '",'
    }

    $rule += '
        ],
        "id": "Notification_Approver_Admin_Assignment",
        "ruleType": "RoleManagementPolicyNotificationRule",
        "target": {
        "caller": "Admin",
        "operations": [
            "All"
        ],
        "level": "Eligibility",
        "targetObjects": null,
        "inheritableSettings": null,
        "enforcedSettings": null
        }
        }
        '

    return $rule
}


<#
.Synopsis
assignee notification when an active assignment is created
.Description
correspond to rule 13 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#notification-rules
.Parameter Notification_ActiveAssignment_Alert
 
hashtable for the settings like: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
.Example
PS> Set-Notification_ActiveAssignment_Assignee -Notification_ActiveAssignment_Assignee @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
set the notification sent to assignee when active assignment is created
.Link
 
.Notes
#>
function Set-Notification_ActiveAssignment_Assignee($Notification_ActiveAssignment_Assignee) {
    $rule = '
                {
                "notificationType": "Email",
                "recipientType": "Requestor",
                "isDefaultRecipientsEnabled": '
+ $Notification_ActiveAssignment_Assignee.isDefaultRecipientEnabled.ToLower() + ',
                "notificationLevel": "'
+ $Notification_ActiveAssignment_Assignee.notificationLevel + '",
                "notificationRecipients": [
                '

    $Notification_ActiveAssignment_Assignee.Recipients | ForEach-Object {
        $rule += '"' + $_ + '",'
    }

    $rule += '
                ],
                "id": "Notification_Requestor_Admin_Assignment",
                "ruleType": "RoleManagementPolicyNotificationRule",
                "target": {
                "caller": "Admin",
                "operations": [
                    "All"
                ],
                "level": "Eligibility",
                "targetObjects": null,
                "inheritableSettings": null,
                "enforcedSettings": null
                }
                }
                '

    return $rule
}


<#
.Synopsis
admin notification when an elligible assignment is created
.Description
correspond to rule 9 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#notification-rules
.Parameter Notification_ActiveAssignment_Alert
hashtable for the settings like: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
.Example
PS> Set-Notification_EligibleAssignment_Alert -Notification_EligibleAssignment_Alert @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
set the notification sent to admin when elligible assignment is created
.Link
 
.Notes
#>

function Set-Notification_EligibleAssignment_Alert($Notification_EligibleAssignment_Alert) {
    write-verbose "Set-Notification_EligibleAssignment_Alert($Notification_EligibleAssignment_Alert)"

    $rule = '
        {
        "notificationType": "Email",
        "recipientType": "Admin",
        "isDefaultRecipientsEnabled": '
+ $Notification_EligibleAssignment_Alert.isDefaultRecipientEnabled.ToLower() + ',
        "notificationLevel": "'
+ $Notification_EligibleAssignment_Alert.notificationLevel + '",
        "notificationRecipients": [
        '

    $Notification_EligibleAssignment_Alert.Recipients | ForEach-Object {
        $rule += '"' + $_ + '",'
    }
    $rule = $rule -replace ".$"
    $rule += '
        ],
        "id": "Notification_Admin_Admin_Eligibility",
        "ruleType": "RoleManagementPolicyNotificationRule",
        "target": {
        "caller": "Admin",
        "operations": [
            "All"
        ],
        "level": "Eligibility",
        "targetObjects": null,
        "inheritableSettings": null,
        "enforcedSettings": null
        }
        }
    '

    write-verbose "end function notif elligible alert"
    return $rule
}


<#
.Synopsis
Approver notification when an elligible assignment is created
.Description
correspond to rule 11 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#notification-rules
.Parameter Notification_EligibleAssignment_Approver
hashtable for the settings like: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
.Example
PS> Set-Notification_EligibleAssignment_Approver -Notification_EligibleAssignment_Approver @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
set the notification sent to approvers when elligible assignment is created
.Link
 
.Notes
#>

function Set-Notification_EligibleAssignment_Approver($Notification_EligibleAssignment_Approver) {
    #write-verbose "function Set-Notification_EligibleAssignment_Approver"
        
    $rule = '
        {
        "notificationType": "Email",
        "recipientType": "Approver",
        "isDefaultRecipientsEnabled": '
+ $Notification_EligibleAssignment_Approver.isDefaultRecipientEnabled.ToLower() + ',
        "notificationLevel": "'
+ $Notification_EligibleAssignment_Approver.notificationLevel + '",
        "notificationRecipients": [
        '

    $Notification_EligibleAssignment_Approver.recipients | ForEach-Object {
        $rule += '"' + $_ + '",'
    }

    $rule += '
        ],
        "id": "Notification_Approver_Admin_Eligibility",
        "ruleType": "RoleManagementPolicyNotificationRule",
        "target": {
        "caller": "Admin",
        "operations": [
            "All"
        ],
        "level": "Eligibility",
        "targetObjects": null,
        "inheritableSettings": null,
        "enforcedSettings": null
        }
        }'

    return $rule
}


<#
.Synopsis
assignee notification when an elligible assignment is created
.Description
correspond to rule 10 here: https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview#notification-rules
.Parameter Notification_EligibleAssignment_Assignee
hashtable for the settings like: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
.Example
PS> Set-Notification_EligibleAssignment_Assignee -Notification_EligibleAssignment_Assignee @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical";"Recipients" = @("email1@domain.com","email2@domain.com")}
 
set the notification sent to assignee when elligible assignment is created
.Link
 
.Notes
#>

function Set-Notification_EligibleAssignment_Assignee {
    [outputType([string])]
    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        $Notification_EligibleAssignment_Assignee
    )

    $rule = '
        {
        "notificationType": "Email",
        "recipientType": "Requestor",
        "isDefaultRecipientsEnabled": '
+ $Notification_EligibleAssignment_Assignee.isDefaultRecipientEnabled.ToLower() + ',
        "notificationLevel": "'
+ $Notification_EligibleAssignment_Assignee.notificationLevel + '",
        "notificationRecipients": [
        '

    $Notification_EligibleAssignment_Assignee.Recipients | ForEach-Object {
        $rule += '"' + $_ + '",'
    }
        
    $rule += '
        ],
        "id": "Notification_Requestor_Admin_Eligibility",
        "ruleType": "RoleManagementPolicyNotificationRule",
        "target": {
        "caller": "Admin",
        "operations": [
            "All"
        ],
        "level": "Eligibility",
        "targetObjects": null,
        "inheritableSettings": null,
        "enforcedSettings": null
        }
        }'


    return $rule
}


<#
      .Synopsis
       Update policy with new rules
      .Description
       Patch $policyID with the rules $rules
      .Parameter PolicyID
       policy ID
      .Parameter rules
        rules
      .Example
        PS> Update-Policy -policyID $id -rules $rules
 
        Update $policyID with rules $rules
      .Link
      
      .Notes
#>

function Update-Policy  {
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        $policyID,
        $rules
    )
    Log "Updating Policy $policyID" -noEcho
    #write-verbose "rules: $rules"
    $scope = "subscriptions/$script:subscriptionID"
    $ARMhost = "https://management.azure.com"
    #$ARMendpoint = "$ARMhost/$scope/providers/Microsoft.Authorization"

    $body = '
        {
            "properties": {
            "scope": "'
+ $scope + '",
            "rules": [
        '
+ $rules +
    '],
          "level": "Assignment"
            }
        }'

    
    $restUri = "$ARMhost/$PolicyId/?api-version=2020-10-01"
   <# write-verbose "`n>> PATCH body: $body"
     
    write-verbose "Patch URI : $restURI"
    $response = Invoke-RestMethod -Uri $restUri -Method PATCH -Headers $authHeader -Body $body -verbose:$false
    #>

    $response = invoke-ARM -restURI $restUri -Method "PATCH" -Body $body
    #
    return $response
}


<#
    .Synopsis
    Export PIM settings of all roles at the subscription scope to a csv file.
    Use the exportFilename parameter to specify the csv file, if not specified default filename
    will be %appdata%\powershell\EasyPIM\Exports\backup_<date>.csv
       
    .Description
    Convert the policy rules to a csv file
     
    .Example
    PS> Export-PIMAzureResourcePolicy -tennantID $tenantID -subscriptionID $subscriptionID -filename "c:\temp\myrole.csv"
 
    Export settings of all roles to file c:\temp\myrole.csv
 
    .Notes
    Author: Loïc MICHEL
    Homepage: https://github.com/kayasax/EasyPIM
     
#>

function Backup-PIMAzureResourcePolicy {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [System.String]
        # Tenant ID
        $tenantID,

        [Parameter(Position = 1, Mandatory = $true)]
        [System.String]
        # subscription id
        $subscriptionID,
        
        [Parameter(Position = 2)]
        [System.String]
        # Filename of the csv to generate
        $exportFilename
        
    )
    try {
        $script:tenantID = $tenantID
        $exports = @()
        $scope = "subscriptions/$subscriptionID"

        $policies = Get-AllPolicies $scope
        
        $policies | ForEach-Object {
            log "exporting $_ role settings"
            #write-verbose $_
            $exports += get-config $scope $_.Trim()
        }
        $date = get-date -Format FileDateTime
        if (!($exportFilename)) { $exportFilename = "$script:_LogPath\EXPORTS\BACKUP_$date.csv" }
        log "exporting to $exportFilename"
        $exportPath = Split-Path $exportFilename -Parent
        #create export folder if no exist
        if ( !(test-path  $exportPath) ) {
            $null = New-Item -ItemType Directory -Path $exportPath -Force
        }
        
        $exports | Select-Object * | ConvertTo-Csv | out-file $exportFilename
    }
    catch {
        MyCatch $_
    }
}

<#
      .Synopsis
        Copy the setting of roles $copyfrom to the role $rolename
      .Description
        Copy the setting of roles $copyfrom to the role $rolename
      .Parameter tenantID
        EntraID tenant ID
      .Parameter subscriptionID
        subscription ID
      .Parameter rolename
        Array of the rolename to update
      .Parameter copyFrom
        We will copy the settings from this role to rolename
      .Example
        PS> Copy-PIMAzureResourcePolicy -subscriptionID "eedcaa84-3756-4da9-bf87-40068c3dd2a2" -rolename contributor,webmaster -copyFrom role1
 
        Copy settings from role role1 to the contributor and webmaster roles
      .Link
      
      .Notes
        Author: Loïc MICHEL
        Homepage: https://github.com/kayasax/EasyPIM
#>

function Copy-PIMAzureResourcePolicy {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        # Tenant ID
        $tenantID,

        [Parameter(Position = 1, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $subscriptionID,

        [Parameter(Position = 2, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String[]]
        $rolename,

        [Parameter(Position = 2, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        $copyFrom
    )
    try {
        $script:tenantID = $tenantID
        Write-Verbose "Copy-PIMAzureResourcePolicy start with parameters: tenantID => $tenantID subscription => $subscriptionID, rolename=> $rolename, copyfrom => $copyFrom"
        $scope = "subscriptions/$subscriptionID"
        $config2 = get-config $scope $copyFrom $true
        
        $rolename | ForEach-Object {
            $config = get-config $scope $_
            Log "Copying settings from $copyFrom to $_"
            [string]$policyID = $config.policyID
            $policyID = $policyID.Trim()
            Update-Policy $policyID $config2
        }
    }
    catch {
        MyCatch $_
    }
        
}


<#
      .Synopsis
        Export the settings of the role $rolename at the subscription scope where subscription = $subscriptionID to $exportFilename, if not set file will be saved in %appdata%\powershell\EasyPIM\exports\
      .Description
        Convert the policy rules to csv
      .Parameter tenantID
        EntraID tenant ID
      .Parameter subscriptionID
        subscription ID
      .Parameter rolename
        Array of the rolename to check
      .Parameter exportFilename
        Filename of the csv to genarate, if not specified default filename will be %appdata%\powershell\EasyPIM\Exports\<datetime>.csv
      .Example
        PS> Export-PIMAzureResourcePolicy -subscriptionID "eedcaa84-3756-4da9-bf87-40068c3dd2a2" -rolename contributor,webmaster -filename "c:\temp\myrole.csv"
 
        Export settings of contributor and webmaster roles to file c:\temp\myrole.csv
      .Link
      
      .Notes
        Author: Loïc MICHEL
        Homepage: https://github.com/kayasax/EasyPIM
     #>

     function Export-PIMAzureResourcePolicy {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [System.String]
        $tenantID,
        [Parameter(Position = 1, Mandatory = $true)]
        [System.String]
        $subscriptionID,
        [Parameter(Position = 2, Mandatory = $true)]
        [System.String[]]
        $rolename,
        [Parameter(Position = 3)]
        [System.String]
        $exportFilename
    )
    try {

        $script:tenantID = $tenantID
   
        Write-Verbose "Export-PIMAzureResourcePolicy start with parameters: subscription => $subscriptionID, rolename=> $rolename, exportFilname => $exportFilename"
        $scope = "subscriptions/$subscriptionID"
        # Array to contain the settings of each selected roles
        $exports = @()

        # run the flow for each role name.
        $rolename | ForEach-Object {
         
            #get curent config
            $config = get-config $scope $_
            $exports += $config
        }
        $date = get-date -Format FileDateTime
        if (!($exportFilename)) { $exportFilename = "$script:_logPath\EXPORTS\$date.csv" }
        log "exporting to $exportFilename"
        $exportPath = Split-Path $exportFilename -Parent
        #create export folder if no exist
        if ( !(test-path  $exportFilename) ) {
            $null = New-Item -ItemType Directory -Path $exportPath -Force
        }
        $exports | Select-Object * | ConvertTo-Csv | out-file $exportFilename
        log "Success! Script ended normaly"
    }
    catch {
        MyCatch $_
    }
}

<#
.Synopsis
EASYPIM
Powershell module to manage PIM Azure Resource Role settings with simplicity in mind
Get-PIMAzureResourcePolicy will return the policy rules (like require MFA on activation) of the selected rolename at the subscription level
Support querrying multi roles at once
 
.Description
  
Get-PIMAzureResourcePolicy will use the ARM REST APIs to retrieve the settings of the role at the subscription scope
 
.PARAMETER tenantID
Tenant ID
 
.PARAMETER subscriptionID
Subscription ID
 
.PARAMETER rolename
Name of the role to check
 
.Example
       PS> Get-PIMAzureResourcePolicy -subscription $subscriptionID -rolename "contributor","webmaster"
 
       show curent config for the roles contributor and webmaster at the subscriptionID scope :
     
.Link
    https://learn.microsoft.com/en-us/azure/governance/resource-graph/first-query-rest-api
    https://learn.microsoft.com/en-us/graph/identity-governance-pim-rules-overview
    Duration ref https://en.wikipedia.org/wiki/ISO_8601#Durations
.Notes
    Homepage: https://github.com/kayasax/easyPIM
    Author: MICHEL, Loic
    Changelog:
    Todo:
    * allow other scopes
#>

function Get-PIMAzureResourcePolicy {
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param (
        
        [Parameter(Position = 0, Mandatory = $true)]
        [System.String]
        # Tenant ID
        $tenantID,
        [Parameter(Position = 1, Mandatory = $true)]
        [System.String]
        # Subscription ID
        $subscriptionID,
        [Parameter(Position = 2, Mandatory = $true)]
        [System.String[]]
        # Array of role name
        $rolename
        
    )
    try {
        $script:tenantID = $tenantID

        Write-Verbose "Get-PIMAzureResourcePolicy start with parameters: subscription => $subscriptionID, rolename=> $rolename"
        $scope = "subscriptions/$subscriptionID"
        $out = @()
        $rolename | ForEach-Object {
            
            #get curent config
            $config = get-config $scope $_
            $out += $config
        }
        Write-Output $out -NoEnumerate
    }
    catch {
        MyCatch $_
    }
    
}

<#
    .Synopsis
        Import the settings from the csv file $path
    .Description
        Convert the csv back to policy rules
    .Parameter tenantID
        Entra ID Tenant ID
    .Parameter subscriptionID
        subscription ID
    .Parameter Path
        path to the csv file
    .Example
        PS> Import-PIMAzureResourcePolicy -tenantID $tenantID -subscriptionID $subscriptionID -path "c:\temp\myrole.csv"
 
        Import settings from file c:\temp\myrole.csv
      
    .Notes
        Author: Loïc MICHEL
        Homepage: https://github.com/kayasax/EasyPIM
     #>

function Import-PIMAzureResourcePolicy {
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $TenantID,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $SubscriptionId,
        
        [Parameter(Mandatory = $true)]
        [String]
        $Path
    )
    
    $script:tenantID = $TenantID
    $script:scope="Subscriptions/$subscriptionID"
    
    #load settings
    Write-Verbose "Importing settings from $path"
    if ($PSCmdlet.ShouldProcess($path, "Importing policy from")) {
    import-setting $Path
    }
    Log "Success, exiting."
}

<#
      .Synopsis
       Set the setting of the role $rolename at the subscription scope where subscription = $subscription
      .Description
       Set the setting of the role $rolename at the subscription scope where subscription = $subscription
  
      .Example
        PS> Set-PIMAzureResourcePolicy -tenantID $tenantID -subscriptionID $subscriptionID -rolename webmaster -ActivationDuration "PT8H"
 
        Limit the maximum PIM activation duration to 8h
      .EXAMPLE
        PS> Set-PIMAzureResourcePolicy -TenantID $tenantID -SubscriptionId $subscriptionID -rolename "contributor" -Approvers @(@{"Id"="00b34bb3-8a6b-45ce-a7bb-c7f7fb400507";"Name"="John";"Type"="user"}) -ApprovalRequired $true
 
        Require activation approval and set John as an approver
 
      .Link
      
      .Notes
        Author: Loïc MICHEL
        Homepage: https://github.com/kayasax/EasyPIM
     #>

function Set-PIMAzureResourcePolicy {
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([bool])]
    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [System.String]
        # Tenant ID
        $tenantID,
        [Parameter(Position = 1, Mandatory = $true)]
        [System.String]
        #subscriptionID
        $subscriptionID,

        [Parameter(Position = 2, Mandatory = $true)]
        [System.String[]]
        #list of role to update
        $rolename,

        [System.String]
        # Maximum activation duration
        $ActivationDuration = $null,
       
        [Parameter(HelpMessage = "Accepted values: 'None' or any combination of these options (Case SENSITIVE): 'Justification, 'MultiFactorAuthentication', 'Ticketing'")]
        [ValidateScript({
                # accepted values: "None","Justification", "MultiFactorAuthentication", "Ticketing"
                # WARNING: options are CASE SENSITIVE
                $script:valid = $true
                $acceptedValues = @("None", "Justification", "MultiFactorAuthentication", "Ticketing")
                $_ | ForEach-Object { if (!( $acceptedValues -Ccontains $_)) { $script:valid = $false } }
                return $script:valid
            })]
        [System.String[]]
        # Activation requirement
        $ActivationRequirement,
        
        [Parameter()]
        [Bool]
        # Is approval required to activate a role? ($true|$false)
        $ApprovalRequired,
    
        [Parameter()]
        # Array of approvers in the format: @(@{"Id"=<ObjectID>;"Name"="John":"Type"="user|group"}, .... )
        $Approvers,
        
        [Parameter()]
        [System.String]
        # Maximum Eligility Duration
        $MaximumEligibilityDuration = $null,
        
        [Parameter()]
        [Bool]
        # Allow permanent eligibility? ($true|$false)
        $AllowPermanentEligibility,
    
        [Parameter()]
        [System.String]
        # Maximum active assignment duration # Duration ref https://en.wikipedia.org/wiki/ISO_8601#Durations
        $MaximumActiveAssignmentDuration = $null,
    
        [Parameter()]
        [Bool]
        # Allow permanent active assignement? ($true|$false)
        $AllowPermanentActiveAssignment,
    
        [Parameter()]
        [System.Collections.Hashtable]
        # Admin Notification when eligible role is assigned
        # Format: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical"};"Recipients" = @("email1@domain.com","email2@domain.com")}
        $Notification_EligibleAssignment_Alert,
        
        [Parameter()]
        [System.Collections.Hashtable]
        # End user notification when eligible role is assigned
        # Format: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical"};"Recipients" = @("email1@domain.com","email2@domain.com")}
        $Notification_EligibleAssignment_Assignee,
        
        [Parameter()]
        [System.Collections.Hashtable]
        # Approver notification when eligible role is assigned
        # Format: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical"};"Recipients" = @("email1@domain.com","email2@domain.com")}
        $Notification_EligibleAssignment_Approver,
        
        [Parameter()]
        [System.Collections.Hashtable]
        # Admin Notification when an active role is assigned
        # Format: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical"};"Recipients" = @("email1@domain.com","email2@domain.com")}
        $Notification_ActiveAssignment_Alert,
    
        [Parameter()]
        [System.Collections.Hashtable]
        # End user Notification when an active role is assigned
        # Format: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical"};"Recipients" = @("email1@domain.com","email2@domain.com")}
        $Notification_ActiveAssignment_Assignee,
    
        [Parameter()]
        [System.Collections.Hashtable]
        # Approver Notification when an active role is assigned
        # Format: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical"};"Recipients" = @("email1@domain.com","email2@domain.com")}
        $Notification_ActiveAssignment_Approver,
    
        [Parameter()]
        [System.Collections.Hashtable]
        # Admin Notification when a is activated
        # Format: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical"};"Recipients" = @("email1@domain.com","email2@domain.com")}
        $Notification_Activation_Alert,
    
        [Parameter()]
        [System.Collections.Hashtable]
        # End user Notification when a role is activated
        # Format: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical"};"Recipients" = @("email1@domain.com","email2@domain.com")}
        $Notification_Activation_Assignee,
    
        [Parameter()]
        [System.Collections.Hashtable]
        # Approvers Notification when a role is activated
        # Format: @{"isDefaultRecipientEnabled"="true|false"; "notificationLevel"="All|Critical"};"Recipients" = @("email1@domain.com","email2@domain.com")}
        $Notification_Activation_Approver
      
    )
    try {
        $p = @()
        $PSBoundParameters.Keys | ForEach-Object {
            $p += "$_ =>" + $PSBoundParameters[$_]
        }
        $p = $p -join ', '
       
        log "Function Set-PIMAzureResourcePolicy is starting with parameters: $p" -noEcho

        $script:subscriptionID = $subscriptionID
        $scope = "subscriptions/$script:subscriptionID"
        $script:tenantID=$tenantID

        #at least one approver required if approval is enable
        # todo chech if a parameterset would be better
        if ($ApprovalRequired -eq $true -and $null -eq $Approvers ) { throw "`n /!\ At least one approver is required if approval is enable, please set -Approvers parameter`n`n" }

        $rolename | ForEach-Object {
            $config = get-config $scope $_
            $rules = @()

            if ($PSBoundParameters.Keys.Contains('ActivationDuration')) {
                $rules += Set-ActivationDuration $ActivationDuration
            }

            if ($PSBoundParameters.Keys.Contains('ActivationRequirement')) {
                $rules += Set-ActivationRequirement $ActivationRequirement
            }

            # Approval and approvers
            if ( ($PSBoundParameters.Keys.Contains('ApprovalRequired')) -or ($PSBoundParameters.Keys.Contains('Approvers'))) {
                $rules += Set-Approval $ApprovalRequired $Approvers
            }

            # eligibility assignement
            if ( $PSBoundParameters.ContainsKey('MaximumEligibilityDuration') -or ( $PSBoundParameters.ContainsKey('AllowPermanentEligibility'))) {
                #if values are not set, use the ones from the curent config
                if (!( $PSBoundParameters.ContainsKey('MaximumEligibilityDuration'))) { $MaximumEligibilityDuration = $config.MaximumEligibilityDuration }
                if (!( $PSBoundParameters.ContainsKey('AllowPermanentEligibility'))) { $AllowPermanentEligibility = $config.AllowPermanentEligibleAssignment }
                $rules += Set-EligibilityAssignment $MaximumEligibilityDuration $AllowPermanentEligibility
            }
     
            #active assignement limits
            if ( $PSBoundParameters.ContainsKey('MaximumActiveAssignmentDuration') -or ( $PSBoundParameters.ContainsKey('AllowPermanentActiveAssignment'))) {
                #if values are not set, use the ones from the curent config
                if (!( $PSBoundParameters.ContainsKey('MaximumActiveAssignmentDuration'))) { $MaximumEligibilityDuration = $config.MaximumActiveAssignmentDuration }
                if (!( $PSBoundParameters.ContainsKey('AllowPermanentActiveAssignment'))) { $AllowPermanentEligibility = $config.AllowPermanentActiveAssignment }
                $rules += Set-ActiveAssignment $MaximumActiveAssignmentDuration $AllowPermanentActiveAssignment
            }

            #################
            # Notifications #
            #################

            # Notif Eligibility assignment Alert
            if ($PSBoundParameters.Keys.Contains('Notification_EligibleAssignment_Alert')) {
                $rules += Set-Notification_EligibleAssignment_Alert $Notification_EligibleAssignment_Alert
            }

            # Notif elligibility assignee
            if ($PSBoundParameters.Keys.Contains('Notification_EligibleAssignment_Assignee')) {
                $rules += Set-Notification_EligibleAssignment_Assignee $Notification_EligibleAssignment_Assignee
            }

            # Notif elligibility approver
            if ($PSBoundParameters.Keys.Contains('Notification_EligibleAssignment_Approver')) {
                $rules += Set-Notification_EligibleAssignment_Approver $Notification_EligibleAssignment_Approver
            }

            # Notif Active Assignment Alert
            if ($PSBoundParameters.Keys.Contains('Notification_ActiveAssignment_Alert')) {
                $rules += Set-Notification_ActiveAssignment_Alert $Notification_ActiveAssignment_Alert
            }
      
            # Notif Active Assignment Assignee
            if ($PSBoundParameters.Keys.Contains('Notification_ActiveAssignment_Assignee')) {
                $rules += Set-Notification_ActiveAssignment_Assignee $Notification_ActiveAssignment_Assignee
            }

            # Notif Active Assignment Approvers
            if ($PSBoundParameters.Keys.Contains('Notification_ActiveAssignment_Approver')) {
                $rules += Set-Notification_ActiveAssignment_Approver $Notification_ActiveAssignment_Approver
            }
        
            # Notification Activation alert
            if ($PSBoundParameters.Keys.Contains('Notification_Activation_Alert')) {
                $rules += Set-Notification_Activation_Alert $Notification_Activation_Alert
            }

            # Notification Activation Assignee
            if ($PSBoundParameters.Keys.Contains('Notification_Activation_Assignee')) {
       
                $rules += Set-Notification_Activation_Assignee $Notification_Activation_Assignee
            }

            # Notification Activation Approvers
            if ($PSBoundParameters.Keys.Contains('Notification_Activation_Approver')) {
                $rules += Set-Notification_Activation_Approver $Notification_Activation_Approver
            }

            # Bringing all the rules together and patch the policy
            $allrules = $rules -join ','
            #Write-Verbose "All rules: $allrules"

            #Patching the policy
            if ($PSCmdlet.ShouldProcess($_, "Udpdating policy")) {
               $null = Update-Policy $config.policyID $allrules
            }
            
        }
        log "Success, policy updated"
        return
    }
    catch {
        MyCatch $_
    }
    
}

#***************************************
#* CONFIGURATION
#***************************************

# LOG TO FILE ( if enable by default it will create a LOGS subfolder in the script folder, and create a logfile with the name of the script )
$script:logToFile = $true
# Where logs are written to
$script:_logPath = "$env:appdata\powershell\easyPIM"

# TEAMS NOTIDICATION
# set to $true if you want to send fatal error on a Teams channel using Webhook see doc to setup
$script:TeamsNotif = $true
#The description will be used as the notification subject
$script:description = "EasyPIM module to manage Azure role setting"
# your Teams Inbound WebHook URL
$script:teamsWebhookURL = "https://microsoft.webhook.office.com/webhookb2/0b9bf9c2-fc4b-42b2-aa56-c58c805068af@72f988bf-86f1-41af-91ab-2d7cd011db47/IncomingWebhook/40db225a69854e49b617eb3427bcded8/8dd39776-145b-4f26-8ac4-41c5415307c7"

#***************************************
#* PRIVATE VARIABLES DON'T TOUCH !!
#***************************************

#from now every error will be treated as exception and terminate the script
$script:_scriptFullName = $MyInvocation.scriptName
$script:_scriptName = Split-Path -Leaf $_scriptFullName
$script:HostFQDN = $env:computername + "." + $env:USERDNSDOMAIN
# ERROR HANDLING
$ErrorActionPreference = "STOP" # make all errors terminating ones so they can be catched

<# moved to invoke-arm function
# Log in first with Connect-AzAccount if not using Cloud Shell
Write-Verbose ">> Connecting to Azure with tenantID $tenantID"
if ( (get-azcontext) -eq $null) { Connect-AzAccount -Tenant $tenantID }
 
# Get access Token
Write-Verbose ">> Getting access token"
$script:token = Get-AzAccessToken
#Write-Verbose ">> token=$($token.Token)"
 
# setting the authentication headers for MSGraph calls
$script:authHeader = @{
    'Content-Type' = 'application/json'
    'Authorization' = 'Bearer ' + $token.Token
}
#>