Microsoft Azure – Security Center Just-in-time Deep Dive

Posted by Lucas Jackson on Sunday, January 19, 2020

DevSecOps

Just-in-time (JIT) is used to secure inbound traffic to your Azure Virtual Machines, reducing exposure to attacks while providing an easy to use mechanism to connect to Virtual Machines (VM) when required.

It is considered a premium feature since you must pay for it. The feature is activated when Azure Security Center (ASC) pricing tier is set to Standard.

Why Would You Use It?

It dramatically reduces the attack footprint against commonly used management ports by blocking traffic to these ports by default.

Ports are only opened upon submitting an access request using the Azure Portal, PowerShell or the REST API.

Who Will Use It?

Anybody who requires management port access to an Azure VM and has the appropriate Role Based Access Control (RBAC) permissions.

As an example, an operator may require Remote Desktop Protocol (RDP) access to perform maintenance tasks on an Azure VM. In this scenario when JIT is enabled, the port would be blocked by default. The operator would submit an access request for the Azure VM and the port would be opened for the operator for a specific time frame.

How Does It Work?

An easy way to look at it, is to view it like a dynamic Network Security Group (NSG). By default when a JIT access policy is defined on a VM, there is a deny rule that is present at all times. When an access request is granted an allow rule is created with a lower priority number (thus higher priority) which grants the temporary access, once the request access duration expires the allow rule is deleted. Any connections that are already established are not being interrupted, however.

How Do You On-Board VMs Into Jit?

There is the ability to on-board VMs to JIT using the Azure Portal using ASC, but you can’t target a specific Resource Group, you have to find the VMs specifically. I developed an automated approach to on-boarding VMs, I found it to provide more simplicity and consistency, and it can be used in a pipeline for automation purposes.

Prerequisites

  • ASC Standard pricing tier (enables the JIT functionality).
  • VM must be Azure Resource Manager type (no Classic resources).
  • VM must have associated NSG.

Azure Portal

  1. Within ASC, go to ADVANCED CLOUD DEFENSE > Just in time VM access.

  1. Click the Recommended tab and select the VMs on enable JIT on.

  1. Accept the defaults by hitting Save, or adjust the profile as required then click Save.

PowerShell

There is a cmdlet available from Microsoft called Set-AzJitNetworkAccessPolicy. This can be used to set access policy, however it is not overly user friendly to use due to the JIT policy array you have to build and pass to the command.

Automated Approach

There is a script available for use which I have written to simplify the on-boarding of VMs in a Subscription. The script is available within my GitHub repository or below in the Appendix.

The script can also target a specific Resource Group (RG) within a subscription.

Usage

This command on-boards all VMs within the specified Subscription and only allows requests from 172.16.1.0/24 and 192.168.1.0/24.

.\Set-JustInTimePolicyForSubscription.ps1 -Subscription "Subscription01" -AllowedSourceAddressPrefix "172.16.1.0/24","192.168.1.0/24"

This command on-boards all VMs within the specified Resource Group and allows requests from all source IPs.

.\Set-JustInTimePolicyForSubscription.ps1 -Subscription "Subscription01" -ResourceGroupName "ResourceGroup01" -AllowedSourceAddressPrefix "*"

JIT Profile Settings Hard Coded in Script

All of these settings can be adjusted as you require.

Ports

The script is hard-coded to configure the following ports, this is the ASC default.

  • 22 (SSH)
  • 3389 (RDP)
  • 5585 (WinRM - HTTP)
  • 5586 (WinRM - HTTPS)
Protocol

The script is hard-coded to configure both TCP/UDP, this is the ASC default.

Duration

The script is hard-coded to configure a 3 hour duration, this is the ASC default.

Any connection established during the 3 hour window that persists after the window expires will stay active. Any new connections will be denied.

Location

The script is hard-coded to use canadacentral.

How Do You Initiate an Access Request?

To initiate an access request, you can use the Azure Portal, PowerShell or the REST API.

After you follow and complete a method below to initiate an access request, you can then connect via RDP as you typically would do. There may be a slight delay, around 20 seconds before the port is opened, so keep this in mind.

Azure Portal

There are two ways within the Azure Portal that a request can be made.

  1. Using Virtual Machines > Overview > Connect

  • VM must be powered on to request access.
  • This will request access to open port 3389 for the VM to all the configured source IPs and for the maximum allowable duration. The configured source IPs and duration are defined by the JIT Access Policy that is applied to the VM.
  1. Within ASC, go to ADVANCED CLOUD DEFENSE > Just in time VM access

  • VM can be powered off when requesting access.
  • This will request access to open port 3389 for the VM using your current IP as the source IP. We have added a justification for the access request as well.

PowerShell

There is a cmdlet available from Microsoft called Start-AzJitNetworkAccessPolicy. This can be used to request access, however it is not overly user friendly to use.

Also it has a severe shortcoming when it comes to passing multiple source IP ranges, currently it does not support the param allowedSourceAddressPrefixes, however the REST API does.

Automated Approach

There is a script available for use which I have written to simplify the access request process. The script is available within my GitHub repository or below in the Appendix.

Usage

This example will request access similar to how the Azure Portal requests access when using the Virtual Machines > Overview > Connect method described above.

.\Open-JustInTimeAccessRequestVm.ps1 -Name "Server01" -Subscription "Subscription01" -ResourceGroupName "ResourceGroup01" -PortNumber 3389

Potential Roadblocks

RBAC Access

As per Microsoft documentation the following RBAC permissions are required for JIT.

To enable a user to Permissions to set
Configure or edit a JIT policy for a VM On the scope of a subscription or Resource Group that is associated with the VM:Microsoft.Security/locations/jitNetworkAccessPolicies/writeOn the scope of a subscription or Resource Group or VM:Microsoft.Compute/virtualMachines/write
Request JIT access to a VM On the scope of a subscription or Resource Group that is associated with the VM:Microsoft.Security/locations/jitNetworkAccessPolicies/initiate/actionOn the scope of a Subscription or Resource Group or VM:Microsoft.Compute/virtualMachines/read

Currently there are no roles outside of Security Admin, Security Manager (Legacy), Contributor and Owner that can request JIT access to a VM.

If we are operating on the best practices of giving least privileges, then we will have to add these to an existing role, or create a new role.

Here is a PowerShell command that can be used to determine what existing roles have explicit access to Microsoft.Security/locations/jitNetworkAccessPolicies/initiate/action, no results are returned for built-in RBAC roles. You may want to use wildcards (*) here as well where you see fit.

Get-AzRoleDefinition | where {$_.Actions -eq "Microsoft.Security/locations/jitNetworkAccessPolicies/initiate/action"}

Here is a PowerShell command to list the jitNetworkAccessPolicies operations

Get-AzProviderOperation Microsoft.Security/locations/jitNetworkAccessPolicies/*

It is probably best to clone the “Virtual Machine Administrator Login” and/or “Virtual Machine User Login” role and add the necessary JIT permissions to initiate a request and grant that role to the users whom are not Contributors or Owners but still require management access to a VM (RDP, SSH, etc).

Here is a PowerShell script to create a custom role as described above.

$role = Get-AzRoleDefinition "Virtual Machine Administrator Login"
$role.Id = $null
$role.Name = "Just-in-time Access Request Initiator Administrator"
$role.Description = $role.Description + " Additionally can initiate Just-in-time (JIT) access requests to Virtual Machines."
$role.Actions.Add("Microsoft.Security/locations/jitNetworkAccessPolicies/read")
$role.Actions.Add("Microsoft.Security/locations/jitNetworkAccessPolicies/initiate/action")
$role.AssignableScopes.Clear()
$role.AssignableScopes.Add("/subscriptions/<subscriptionId>")
New-AzRoleDefinition -Role $role

Inbound Rule Reprioritize

When applying a JIT access policy to a VM or a series of VMs, Microsoft will reprioritize inbound rules within the NSG. Through testing it was determined that:

  • Preexisting inbound rules are moved to priority 1000 and up.
  • The default inbound deny rule priority starts at 1014, incrementing by 1 each time.
    • Unless you have more 15 or more preexisting inbound rules, in which case the JIT deny rules will begin at a higher priority number.
  • The default inbound allow rule priority starts at 100, incrementing by 1 each time.

If a VM has NSG applied at both the Subnet level and the NIC level, both NSG have the same rules added. This is proper and makes sense, but makes sense to document the behavior.

Before

By creating a bunch of bogus inbound rules with varying priorities we can illustrate the before and after results, as shown in the screen captures below.

After

You can see how it has altered the preexisting inbound rules priorities significantly, however ordering is kept the same.

VM Moved to a New Resource Group

If a VM is moved to a new RG after a JIT access policy was applied, the VM will be removed from the existing policy and a new JIT access policy must be applied.

There may be manual cleanup required to the inbound security rules for the NSG if this occurs as old rules may not be deleted.

Rule Sprawl

Since JIT adds a specific deny rule for each VM, the rules within an NSG can grow quickly if you have a lot of VMs utilizing the same NSG.

Alteration of Existing Inbound Rules

Some existing inbound rules may be required to be modified to have the ports defined under scope removed from the rule(s).

Delete JIT Access Policies

If there is some reason to remove all JIT Access Policies the following command can be used:

Get-AzJitNetworkAccessPolicy | Remove-AzJitNetworkAccessPolicy

It can also be narrowed down to a Resource Group:

Get-AzJitNetworkAccessPolicy -ResourceGroupName "ResourceGroup01" | Remove-AzJitNetworkAccessPolicy

Appendix

Set-JustInTimePolicyForSubscription.ps1

<#
    .SYNOPSIS 
      Configures a Just-in-time (JIT) access policy on all Virtual Machines (VM) within a subscription.
    .PARAMETER Subscription
      Subscription to target.
    .PARAMETER AllowedSourceAddressPrefixes
      Source subnets allowed for JIT access profile.
    .PARAMETER ResourceGroupName
      Resource Group Name to target. (Optional)
    .DESCRIPTION
      In short, JIT is basically a dynamic Network Security Group (NSG) rule. Once a VM has a JIT policy applied, a user must request access to a VM.
      If the user has the appropriate Role Based Access Control (RBAC), then the request will be granted, otherwise it will be denied.
      Azure Security Center (ASC) automatically configures the NSG and Azure Firewall to allow inbound traffic to the selected ports and requested source IP addresses or ranges, for the amount of time that was specified.
      After the time has expired, Security Center restores the NSGs to their previous states. Those connections that are already established are not being interrupted, however.
      More information is available here: https://docs.microsoft.com/en-us/azure/security-center/security-center-just-in-time
      RBAC permissions needed to configure and use JIT are available here: https://docs.microsoft.com/en-us/azure/security-center/security-center-just-in-time#permissions-needed-to-configure-and-use-jit
      This script will query all VMs within the selected subscription and attempt to apply the JIT access policy below to each and every VM that is found.
      JIT policy breakdown:
      - Ports: 22, 3389, 5585, 5586
      - Protocol: *
      - AllowedSourceAddressPrefix: Allowed Source Subnets for JIT access profile, provided by input parameter AllowedSourceAddressPrefixes.
      - MaxRequestAccessDuration: 3 Hours
      If you need to change any of these values (Ports, Protocol, MaxRequestAccessDuration), edit the function Set-JustInTimePolicyForVM in this script.
      Location is also hard-coded to canadacentral, change it if required.
      Since JIT policies work at the Resource Group (RG) level, we loop through each RG looking for VMs and build a JIT policy based on this logic.
      JIT Access Policy must be named "default" for ASC to treat it as configured JIT enabled VM.
      Pre-requisites: Azure Security Center needs to be enabled (Standard plan), this enables the JIT functionality.
      If it's not set to Standard plan, script will throw "Subscription is not in the Standard or Standard Trial subscription plan. Please upgrade to use this feature." when trying to create JIT policy.
      VMs also require the following: 
      - Resource type: Azure Resource Manager (no Classic resources)
      - Associated NSG at subnet or nic level, or both.
      Through testing it was determined that:
      - The default inbound allow rule priority start at 100, incrementing by 1 each time.
      - The default inbound deny rule priority starts at 1014, incrementing by 1 each time.
      Warning: When applying a JIT access policy to a VM or a series of VMs, Microsoft will reprioritize inbound rules within the NSG.
      Warning: If a VM is moved to a new RG after a JIT access policy was applied, the VM will be removed from the existing policy and a new JIT access policy must be applied.
      Also, there may be manual cleanup required to the inbound security rules for the NSG if this occurs.
      To delete all JIT access policies within a Subsciption, use the following command: Get-AzJitNetworkAccessPolicy | Remove-AzJitNetworkAccessPolicy
      To delete all JIT access policies within a Resource Group, use the following command: Get-AzJitNetworkAccessPolicy -ResourceGroupName "ResourceGroup01" | Remove-AzJitNetworkAccessPolicy
      Required modules:
      - Az.Accounts
      - Az.Compute
      - Az.Network
      - Az.Resources
      - Az.Security
    .NOTES
      Version: 1.0
      Author:  Lucas Jackson
      Date:    1/18/2020
    .EXAMPLE
      .\Set-JustInTimePolicyForSubscription.ps1 -Subscription "Subscription01" -AllowedSourceAddressPrefix "172.16.1.0/24","192.168.1.0/24"
      This command onboards all Virtual Machines within the specified Subscription and only allows requests from 172.16.1.0/24 and 192.168.1.0/24.
    .EXAMPLE
      .\Set-JustInTimePolicyForSubscription.ps1 -Subscription "Subscription01" -ResourceGroupName "ResourceGroup01" -AllowedSourceAddressPrefix "*"
      This command onboards all Virtual Machines within the specified Resource Group and allows requests from all source IPs.
    .LINK
      https://www.devopspertise.com
#>
[CmdLetBinding()]
  param(
    [Parameter(Mandatory = $true)]
    [ValidateNotNullorEmpty()]
    [String]$Subscription,

    [Parameter(Mandatory = $true)]
    [ValidateNotNullorEmpty()]
    [String[]]$AllowedSourceAddressPrefixes,

    [Parameter(Mandatory = $false)]
    [ValidateNotNullorEmpty()]
    [String]$ResourceGroupName
  )

$ErrorActionPreference = "Stop"

if($ResourceGroupName) {
  Write-Host "$Subscription subscription and $ResourceGroupName resource group selected"
}
else {
  Write-Host "$Subscription subscription selected"
}

function Set-JustInTimePolicyForVM {
param(
      [Parameter(Mandatory = $true)]
      [ValidateNotNullorEmpty()]
      [String[]] $AllowedSourceAddressPrefixes,

      [Parameter(Mandatory = $true)]
      [ValidateNotNullorEmpty()]
      [String]$Id
)
  process
    {
      if($AllowedSourceAddressPrefixes -eq "*") {
        $JitPolicy = (@{
          Id=$Id
          Ports=(@{
            number=22;
            protocol="*";
            allowedSourceAddressPrefix=@("*");
            maxRequestAccessDuration="PT3H"},
            @{
            number=3389;
            protocol="*";
            allowedSourceAddressPrefix=@("*");
            maxRequestAccessDuration="PT3H"},
            @{
            number=5585;
            protocol="*";
            allowedSourceAddressPrefix=@("*");
            maxRequestAccessDuration="PT3H"},
            @{
            number=5586;
            protocol="*";
            allowedSourceAddressPrefix=@("*");
            maxRequestAccessDuration="PT3H"})})
      } 
      else {
        $JitPolicy = (@{
          Id=$Id
          Ports=(@{
            number=22;
            protocol="*";
            allowedSourceAddressPrefixes=$AllowedSourceAddressPrefixes;
            maxRequestAccessDuration="PT3H"},
            @{
            number=3389;
            protocol="*";
            allowedSourceAddressPrefixes=$AllowedSourceAddressPrefixes;
            maxRequestAccessDuration="PT3H"},
            @{
            number=5585;
            protocol="*";
            allowedSourceAddressPrefixes=$AllowedSourceAddressPrefixes;
            maxRequestAccessDuration="PT3H"},
            @{
            number=5586;
            protocol="*";
            allowedSourceAddressPrefixes=$AllowedSourceAddressPrefixes;
            maxRequestAccessDuration="PT3H"})})
      }
      
      $JitPolicyArr=@($JitPolicy)
      return $JitPolicyArr
  }
}

# Set Context, see if we have access
# Get all Virtual Machines
# Loop through Virtual Machines applying the JIT policy defined
Try {
  # Set Context
  Set-AzContext -Subscription $Subscription

  # Get Resource Groups within Context
  # Check if ResourceGroupName param has been passed or not
  if ($ResourceGroupName) {
    $allRgs = $ResourceGroupName
  }
  else {
    $allRgs = Get-AzResourceGroup
  }

  # Get Virtual Machines within Resource Group
  foreach($rg in $allRgs) {
    $setJitPolicyArr = $null

    if ($ResourceGroupName) {
      $resourceGroupNameCurrent = $ResourceGroupName
    }
    else {
      $resourceGroupNameCurrent = $rg.ResourceGroupName
    }
    
    Write-Host "`nGetting VMs in $resourceGroupNameCurrent"
    $allVmsRg = Get-AzVm -ResourceGroup $resourceGroupNameCurrent

      foreach($vm in $allVmsRg) {
        $nsgFound = $false

        Write-Host $vm.Name $vm.Id
        foreach($nic in $vm.NetworkProfile.NetworkInterfaces.Id) {
          Write-Host "NIC: $nic"
          $existNsgVm = Get-AzNetworkInterface | Where-Object Id -eq $nic

          if ($existNsgVm.NetworkSecurityGroup.Id) {
            Write-Host "NSG NIC:" $existNsgVm.NetworkSecurityGroup.Id`n
            $nsgFound = $true
            break
          }

          $vmIpConfig = Get-AzNetworkInterface | Where-Object Id -eq $nic | Get-AzNetworkInterfaceIpConfig
          $allVirtualNetwork = Get-AzVirtualNetwork

          foreach($virtualNetwork in $allVirtualNetwork) {
            foreach($subnet in $virtualNetwork.Subnets) {
              if($subnet.Id -eq $vmIpConfig.Subnet.Id -and $subnet.NetworkSecurityGroup.Id) {
                Write-Host "NSG SUBNET:" $subnet.NetworkSecurityGroup.Id`n
                $nsgFound = $true
                break
              }
            }
          }
        } 
        
        if($nsgFound) {
          $setJitPolicy = Set-JustInTimePolicyForVM -Id $vm.Id -AllowedSourceAddressPrefixes $allowedSourceSubnets
          $setJitPolicyArr+=@($setJitPolicy)
        }
        else {
          Write-Host "WARNING: No NSG found for" $vm.Name "<--- resource ignored`n" -ForegroundColor "Yellow"
        }
      }
      if($setJitPolicyArr) {
        Set-AzJitNetworkAccessPolicy -Kind "Basic" -Location "canadacentral" -Name "default" -ResourceGroupName $resourceGroupNameCurrent -VirtualMachine $setJitPolicyArr
      }
      else {
        Write-Host "No suitable VMs found for onboarding in resource group $resourceGroupNameCurrent"
      }
  }
}
Catch {
  Write-Host $error[0] -ForegroundColor "Red"
  exit 10
}

Open-JustInTimeAccessRequestVm.ps1

<#
    .SYNOPSIS 
      Starts a 3 hour Just-in-time (JIT) session for a virtual machine.
    .PARAMETER Subscription
      Subscription to target.
    .PARAMETER ResourceGroupName
      Name of the resource group where the VM resides.
    .PARAMETER Name
      Name of the Virtual Machine (VM).
    .PARAMETER PortNumber
      Port number that you would like to open (22,3389,5585,5586).
    .DESCRIPTION
      In short, JIT is basically a dynamic Network Security Group (NSG) rule. Once a VM has a JIT policy applied, a user must request access to a VM.
      If the user has the appropriate Role Based Access Control (RBAC), then the request will be granted, otherwise it will be denied.
      Azure Security Center (ASC) automatically configures the NSG and Azure Firewall to allow inbound traffic to the selected ports and requested source IP addresses or ranges, for the amount of time that was specified.
      After the time has expired, Security Center restores the NSGs to their previous states. Those connections that are already established are not being interrupted, however.

      More information is available here: https://docs.microsoft.com/en-us/azure/security-center/security-center-just-in-time
      RBAC permissions needed to configure and use JIT are available here: https://docs.microsoft.com/en-us/azure/security-center/security-center-just-in-time#permissions-needed-to-configure-and-use-jit

      This script will send a "Request Access" operation for a VM that has a JIT access policy enabled.
      
      Location is hard-coded to canadacentral, change it if required.

      This script is for users who want to request access programmatically versus using the Azure Portal.
      You can request access using the Azure Portal by going to the VM and clicking Connect, or by going to ASC and clicking on Just in time VM access.

      Common use cases for this script is when you want to use the management ports of a VM, eg. SSH (port 22) or RDP (port 3389). By default the access request asks that the port be opened for a duration of 3 hours.
      Any connection established during the 3 hour window that persists after the window expires will stay active.    

      Start-AzJitNetworkAccessPolicy cmdlet currently doesn't support the param allowedSourceAddressPrefixes, however the REST API does, therefore we are forced to use a REST API instead of the cmdlet.

      Required modules:
      - Az.Accounts
      - Az.Compute
      - Az.Security
    .NOTES
      Version: 1.0
      Author:  Lucas Jackson
      Date:    1/18/2020
    .EXAMPLE
     .\Open-JustInTimeAccessRequestVm.ps1 -Subscription "Subscription01" -ResourceGroupName "ResourceGroup01" -Name "Server01" -PortNumber 3389

      Requests access to a Virtual Machine over port 3389 (Remote Desktop Port).
    .LINK
      https://www.devopspertise.com
#>

param(
      [String]$Location = "canadacentral",

      [Parameter(Mandatory = $true)]
      [ValidateNotNullorEmpty()]
      [String]$Subscription,
      
      [Parameter(Mandatory = $true)]
      [ValidateNotNullorEmpty()]
      [String]$ResourceGroupName,

      [Parameter(Mandatory = $true)]
      [ValidateNotNullorEmpty()]
      [String]$Name,

      [Parameter(Mandatory = $true)]
      [ValidateSet(22,3389,5585,5586)]
      [ValidateNotNullorEmpty()]
      [Int]$PortNumber

)

$ErrorActionPreference = "Stop"

Write-Host "$Subscription subscription selected`n"

Try {
      # Set Context
      $azContext = Set-AzContext -Subscription $Subscription
      $subscriptionId = $azContext.Subscription.Id

      # Get Bearer token for REST API from the current authenticated session
      $azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
      $profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azProfile)
      Write-Host "Getting Bearer token for current session...`n"
      $token = $profileClient.AcquireAccessToken($azContext.Tenant.TenantId)
      $tokenBearer = ('Bearer {0}' -f ($token.AccessToken))

      # Get the VM
      $vmTarget = Get-AzVm -Name $Name -ResourceGroup $ResourceGroupName

      # Get the ASC JIT Access Policy for the Resource Group
      Write-Host "Getting corresponding Azure Security Center Just-in-time access policy for $Name"
      $jitPolicy = Get-AzJitNetworkAccessPolicy -ResourceGroupName $ResourceGroupName -Name "default" -Location $Location

      # Determine if there is a JIT Access Policy associated to the VM
      # Since each entry is unique, we can use IndexOf to determine the allowedSourceAddressPrefix(es)
      $arrayJitPolicy = $jitPolicy.VirtualMachines.Id
      foreach($vm in $arrayJitPolicy) {
            if($vm -eq $vmTarget.Id) {
                  $foundPolicy = $true
                  $arrayIndexVM = $arrayJitPolicy.IndexOf($vm)
                  $arrayAllowedSourcePorts = $jitPolicy.VirtualMachines[$arrayIndexVM].Ports.Number
                  foreach($port in $arrayAllowedSourcePorts) {
                        if($port -eq $PortNumber) {
                              $arrayIndexPort = $arrayAllowedSourcePorts.IndexOf($port)
                              $arrayAllowedSourceAddressPrefix = $jitPolicy.VirtualMachines[$arrayIndexVM].Ports[$arrayIndexPort].AllowedSourceAddressPrefix
                              $arrayAllowedSourceAddressPrefixes = $jitPolicy.VirtualMachines[$arrayIndexVM].Ports[$arrayIndexPort].AllowedSourceAddressPrefixes
                              if($foundPolicy -and ($arrayAllowedSourceAddressPrefix -or $arrayAllowedSourceAddressPrefixes)) {
                                    Write-Host ("Found: " + $jitPolicy.Id)
                                    if($arrayAllowedSourceAddressPrefix) {
                                          Write-Host "AllowedSourceAddressPrefix: $arrayAllowedSourceAddressPrefix"
                                    } else {
                                          Write-Host "AllowedSourceAddressPrefixes: $arrayAllowedSourceAddressPrefixes"
                                    }
                                    $justication = "Automated JIT Access Request"
                              }
                              break
                        }
                  }
                  break
            }
      }
      if(!$foundPolicy) {
            Write-Host "`nERROR: Could not find corresponding Azure Security Center Just-in-time access policy for $Name" -ForegroundColor "Red"
            exit 20
      }
            
      # Build the JIT policy request
      # Build request header and body to pass with REST API request
      if($arrayAllowedSourceAddressPrefix) {
            $JitPolicyVm = (@{
                  virtualMachines=(@(@{
                  id=$vmTarget.Id
                  ports=(@(@{
                  number=$PortNumber;
                  endTimeUtc=[DateTime]::UtcNow.AddHours(3);
                  allowedSourceAddressPrefix=$arrayAllowedSourceAddressPrefix}))}))
                  justification=$justication})
      }
      elseif($arrayAllowedSourceAddressPrefixes) {
            $JitPolicyVm = (@{
                  virtualMachines=(@(@{
                  id=$vmTarget.Id
                  ports=(@(@{
                  number=$PortNumber;
                  endTimeUtc=[DateTime]::UtcNow.AddHours(3);
                  allowedSourceAddressPrefixes=@($arrayAllowedSourceAddressPrefixes)}))}))
                  justification=$justication})
      }
      else {
            Write-Host "`nERROR: Could not find AllowedSourceAddressPrefix(es) for port $PortNumber" -ForegroundColor "Red"
            exit 21
      }

      $requestBody = $JitPolicyVm | ConvertTo-Json -Depth 5

      $requestHeader = @{
            'Authorization'=$tokenBearer
      }

      # Request access to VM using REST API
      $uri = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Security/locations/$Location/jitNetworkAccessPolicies/default/initiate?api-version=2015-06-01-preview"
      Write-Host "`nRequesting Just-in-time access to $Name for 3 hours"
      Write-Host $requestBody`n
      Write-Host $uri`n
      Invoke-RestMethod -Uri $uri -Headers $requestHeader -Body $requestBody -Method POST -ContentType 'application/json'

      # Request access to the VM using PowerShell
      # Cannot use this method since cmdlet does not support allowedSourceAddressPrefixes at time of script creation
      #$JitPolicyArr=@($JitPolicy)
      #Write-Host "Requesting Just-in-time access to $Name for 3 hours"
      #Start-AzJitNetworkAccessPolicy -ResourceId $jitPolicy.Id -VirtualMachine $JitPolicyArr
}
Catch {
      Write-Host $error[0] -ForegroundColor "Red"
      exit 10
}