AzureCoreManagement.ps1

# This script contains functions for Azure Core Management API

# Return the classic administrators of the given subscription
# May 30th 2020
function Get-AzureClassicAdministrators
{
<#
    .SYNOPSIS
    Returns classic administrators of the given Azure subscription

    .DESCRIPTION
    Returns classic administrators of the given Azure subscription

    .Example
    Get-AADIntAzureClassicAdministrators

    emailAddress role
    ------------ ----
    admin@company.onmicrosoft.com ServiceAdministrator;AccountAdministrator
    co-admin@comapny.com CoAdministrator

    .Example
    C:\PS>Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache
    C:\PS>Get-AADIntAzureClassicAdministrators

    emailAddress role
    ------------ ----
    admin@company.onmicrosoft.com ServiceAdministrator;AccountAdministrator
    co-admin@comapny.com CoAdministrator

   
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$Subscription
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
        }

        # Invoke the command
        $response=Invoke-RestMethod -UseBasicParsing -Method get -Uri "https://management.azure.com/subscriptions/$Subscription/providers/Microsoft.Authorization/classicAdministrators?api-version=2015-06-01" -Headers $headers

        # Return
        $response.value.properties
    }
}

# Elevates the current Global Admin to Azure User Access Administrator
# May 30th 2020
function Grant-AzureUserAccessAdminRole
{
<#
    .SYNOPSIS
    Elevates the current authenticated Global Admin to Azure User Access Administrator

    .DESCRIPTION
    Elevates the current authenticated Global Admin to Azure User Access Administrator.
    This allows the admin for instance to manage all role assignments in all subscriptions of the tenant.

    .Example
    Grant-AADIntAzureUserAccessAdminRole

    .Example
    $at=Get-AADIntAccessTokenForAzureCoreManagement
    C:\PS>Grant-AADIntAzureUserAccessAdminRole -AccessToken $at

   
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
        }

        # Invoke the command. Returns 200 OK if successfull
        Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://management.azure.com/providers/Microsoft.Authorization/elevateAccess?api-version=2015-07-01" -Headers $headers
    }
}


# Lists user's Subscriptions
# Jun 2nd 2020
function Get-AzureSubscriptions
{
<#
    .SYNOPSIS
    Lists the user's Azure subscriptions

    .DESCRIPTION
     Lists the user's Azure subscriptions

    .Example
    $at=Get-AADIntAccessTokenForAzureCoreManagement
    C:\PS>Get-AADIntAzureSubscriptions -AccessToken $at

    subscriptionId displayName state
    -------------- ----------- -----
    867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 MyAzure001 Enabled
    99fccfb9-ed41-4179-aaf5-93cae2151a77 Pay-as-you-go Enabled
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
        }

        # Invoke the command. Returns 200 OK if successfull
        $response = Invoke-RestMethod -UseBasicParsing -Method Get -Uri "https://management.azure.com/subscriptions?api-version=2016-06-01" -Headers $headers

        # Return
        foreach($value in $response.value)
        {
            $value | Select subscriptionId,displayName,state
        }
        
    }
}

# Lists azure subscription resource groups
# Jun 2nd 2020
function Get-AzureResourceGroups
{
<#
    .SYNOPSIS
    Lists Azure subscription ResourceGroups

    .DESCRIPTION
    Lists Azure subscription ResourceGroups

    .Example
    Get-AADIntAzureResourceGroups -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0

    name location tags
    ---- -------- ----
    Production westus Production
    Test eastus Test

    .Example
    $at=Get-AADIntAccessTokenForAzureCoreManagement
    C:\PS>Get-AADIntAzureSubscriptions -AccessToken $at

    subscriptionId displayName state
    -------------- ----------- -----
    867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 MyAzure001 Enabled

    C:\PS>Get-AADIntAzureResourceGroups -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0

    name location tags
    ---- -------- ----
    Production westus Production
    Test eastus Test
   
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$SubscriptionId
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
        }

        # Invoke the command.
        $response = Invoke-RestMethod -UseBasicParsing -Method Get -Uri "https://management.azure.com/subscriptions/$SubscriptionId/resourcegroups?api-version=2019-10-01" -Headers $headers

        # Return
        foreach($value in $response.value)
        {
            $value | Select name,location,tags
        }
        
    }
}


# Lists azure subscription VMs
# Jun 2nd 2020
function Get-AzureVMs
{
<#
    .SYNOPSIS
    Lists Azure subscription VMs

    .DESCRIPTION
    Lists Azure subscription VMs and shows information including server name, VM OS and size, and admin user name.

    .Example
    Get-AADIntAzureVMs -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0

    resourceGroup name location id computerName adminUserName vmSize OS
    ------------- ---- -------- -- ------------ ------------- ------ --
    PRODUCTION Client westus c210d38b-3346-41d3-a23d-27988315825b Client AdminUSer Standard_A2_v2 Windows
    PRODUCTION DC westus 9b8f8753-196f-4f24-847a-e5bcb751936d DC AdminUSer Standard_DS1_v2 Windows
    PRODUCTION Exchange westus a12ffb24-a69e-4ce9-aff3-275f49bba315 Exchange AdminUSer Standard_DS2_v2 Windows
    PRODUCTION Server1 westus c7d98db7-ccb5-491f-aaeb-e71f0df478b6 Server1 AdminUSer Standard_DS1_v2 Windows
    TEST Server2 eastus ae34dfcc-ad89-4e53-b0b4-20d453bdfcef Server2 AdminUSer Standard_DS1_v2 Windows
    TEST Server3 eastus f8f6a7c5-9927-47f9-a790-84c866f5719c Server3 AzureUser Standard_B1ms Linux

    .Example
    $at=Get-AADIntAccessTokenForAzureCoreManagement
    C:\PS>Get-AADIntAzureSubscriptions -AccessToken $at

    subscriptionId displayName state
    -------------- ----------- -----
    867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 MyAzure001 Enabled

    C:\PS>Get-AADIntAzureVMs -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0

    resourceGroup name location id computerName adminUserName vmSize OS
    ------------- ---- -------- -- ------------ ------------- ------ --
    PRODUCTION Client westus c210d38b-3346-41d3-a23d-27988315825b Client AdminUSer Standard_A2_v2 Windows
    PRODUCTION DC westus 9b8f8753-196f-4f24-847a-e5bcb751936d DC AdminUSer Standard_DS1_v2 Windows
    PRODUCTION Exchange westus a12ffb24-a69e-4ce9-aff3-275f49bba315 Exchange AdminUSer Standard_DS2_v2 Windows
    PRODUCTION Server1 westus c7d98db7-ccb5-491f-aaeb-e71f0df478b6 Server1 AdminUSer Standard_DS1_v2 Windows
    TEST Server2 eastus ae34dfcc-ad89-4e53-b0b4-20d453bdfcef Server2 AdminUSer Standard_DS1_v2 Windows
    TEST Server3 eastus f8f6a7c5-9927-47f9-a790-84c866f5719c Server3 AzureUser Standard_B1ms Linux

   
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$SubscriptionId
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
        }
        
        # Invoke the command
        $response = Invoke-RestMethod -UseBasicParsing -Method Get -Uri "https://management.azure.com/subscriptions/$SubscriptionId/providers/Microsoft.Compute/virtualMachines?api-version=2019-12-01" -Headers $headers

        # Return
        foreach($value in $response.value)
        {
            $attributes=[ordered]@{
                ResourceGroup = $value.id.split("/")[4]
                Name =          $value.name
                Location =      $value.location
                Id =            $value.properties.vmId
                #license = $value.properties.licenseType
                ComputerName=   $value.properties.osProfile.computerName
                AdminUserName=  $value.properties.osProfile.adminUserName
                VMSize =        $value.properties.hardwareProfile.vmSize
                OS = ""
            }
            if($value.properties.osProfile.WindowsConfiguration)
            {
                $attributes["OS"] = "Windows"
            }
            if($value.properties.osProfile.linuxConfiguration)
            {
                $attributes["OS"] = "Linux"
            }
            New-Object psobject -Property $attributes
        }
        
    }
}


# Runs a given script on the given Azure VM
# Jun 2nd 2020
function Invoke-AzureVMScript
{
<#
    .SYNOPSIS
    Runs a given script on the given Azure VM

    .DESCRIPTION
    Runs a given script on the given Azure VM and prints out the response. Note! Returns only ascii, so any non-ascii character is not shown correctly.
    Multi-line scripts are supported. Use `n as a line separator.

    .Example
    Invoke-AADIntAzureVMScript -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 -ResourceGroup TEST -Server Server2 -Script "whoami"

    [stdout]
    nt authority\system

    [stderr]

    .Example
    Invoke-AADIntAzureVMScript -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 -ResourceGroup TEST -Server Server2 -Script "whoami`nGet-Process 123123123"

    [stdout]
    nt authority\system

    [stderr]
    Get-Process : Cannot find a process with the name "123123123". Verify the process name and call the cmdlet again.
    At C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\1.1.5\Downloads\script42.ps1:2 char:1
    + Get-Process 123123123
    + ~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : ObjectNotFound: (123123123:String) [Get-Process], ProcessCommandException
        + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand

    .Example
    Invoke-AADIntAzureVMScript -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 -ResourceGroup TEST -Server Server3 -Script "whoami" -VMType Linux

    Enable succeeded:
    [stdout]
    root

    [stderr]

    .Example
    Invoke-AADIntAzureVMScript -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 -ResourceGroup PRODUCTION -Server Server2 -Script "Get-Process"

    [stdout]
        727 36 14132 27092 5.94 396 0 svchost
        936 29 69796 76820 7.91 400 0 svchost
        664 22 15664 27432 39.39 464 0 svchost
        839 23 6856 24352 0.91 792 0 svchost
        785 17 4792 10968 4.75 892 0 svchost
        282 13 3020 9324 7.41 1052 0 svchost
       1889 96 38548 72480 24.86 1216 0 svchost
        642 35 8928 28452 0.50 1236 0 svchost
        519 24 19480 37620 4.08 1376 0 svchost
        411 17 15440 18076 29.81 1392 0 svchost
        833 41 10676 25512 2.02 1424 0 svchost
        317 11 2000 8840 0.08 1432 0 svchost
        380 31 7324 16320 0.39 1584 0 svchost
        211 12 1876 7524 0.22 1808 0 svchost
        199 9 1596 6916 0.00 1968 0 svchost
        200 10 2308 8344 0.06 2188 0 svchost
        146 8 1472 7144 0.06 3000 0 svchost
        468 21 6516 31128 0.33 3140 2 svchost
        173 9 4332 12968 0.72 3208 0 svchost
       2061 0 192 156 11.45 4 0 System
        340 17 3964 17324 0.13 3416 2 TabTip
        413 24 13016 34008 0.25 4488 2 TabTip
        103 7 1264 4756 0.00 3264 2 TabTip32
        216 22 4864 14260 0.08 1272 2 taskhostw
        446 24 17080 22096 0.39 2796 0 taskhostw
        150 9 1664 8984 0.03 1196 0 VSSVC
        946 45 62896 78976 13.22 2068 0 WaAppAgent
        119 6 1504 5800 0.02 4152 0 WaSecAgentProv
        646 41 45220 68180 85.78 2088 0 WindowsAzureGuestAgent
        131 9 2252 8344 0.03 3868 0 WindowsAzureNetAgent
        174 11 1548 6916 0.11 552 0 wininit
        234 11 2588 11160 0.09 612 1 winlogon
        266 12 2456 10120 0.08 3428 2 winlogon
        178 10 2776 8368 0.02 4052 0 WmiPrvSE

   [stderr]
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$SubscriptionId,
        [Parameter(Mandatory=$True)]
        [String]$ResourceGroup,
        [Parameter(Mandatory=$True)]
        [String]$Server,
        [Parameter(Mandatory=$True)]
        [String]$Script,
        [ValidateSet("Windows","Linux")]
        [String]$VMType="Windows"

    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "x-ms-command-name" = "Microsoft_Azure_Automation."
        }
        
        # Define the script type
        if($VMType -eq "Windows")
        {
            $scriptType="Power"
        }

        # Create the body
        $body = @{
            "commandId" = "Run$($scriptType)ShellScript"
            "script" = @($Script)
        }

        # Invoke the command
        $response = Invoke-WebRequest -UseBasicParsing -Method Post -Uri "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroup/providers/Microsoft.Compute/virtualMachines/$Server/runCommand?api-version=2018-04-01" -Headers $headers -Body ($body | ConvertTo-Json) -ContentType "application/json; charset=utf-8"

        # Get the async operation url
        $async = $response.Headers["Azure-AsyncOperation"]
        Write-Verbose "Azure-AsyncOperation: $async"

        while($status = Invoke-RestMethod -UseBasicParsing -Uri $async -Headers $headers)
        {
            if($status.status -eq "InProgress")
            {
                # Still pending, wait for two seconds
                Start-Sleep -Seconds 5
            }
            else
            {
                if($status.status -eq "Succeeded")
                {
                    # Script was executed successfully - but we don't know the actual result
                    $value = $status.properties.output.value

                    # Loop through the output streams
                    foreach($output in $value)
                    {
                        if($output.code.Contains("StdOut"))
                        {
                            Write-Host "[stdout]"
                            Write-Host $output.message
                        }
                        elseif($output.code.Contains("StdErr"))
                        {
                            Write-Host "`n[stderr]"
                            Write-Host $output.message
                        }
                        else
                        {
                            Write-Host $output.message 
                        }
                    }

                }
                else
                {
                    Write-Error "The script failed"
                }
                
                break
            }
        }
        
    }
}



# Runs a given script on the given Azure VM
# Jun 3rd 2020
function Get-AzureVMRdpSettings
{
<#
    .SYNOPSIS
    Gets RDPSettings of the given Azure VM

    .DESCRIPTION
    Gets RDPSettings of the given Azure VM

    .Example
    Get-AADIntAzureVMRdpSettings -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 -ResourceGroup PRODUCTION -Server Server2

    Not domain joined
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\PortNumber: 3389
    HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\fDenyTSConnections:
    HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\KeepAliveEnable: 1
    HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\KeepAliveInterval: 1
    HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\KeepAliveTimeout: 1
    HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\fDisableAutoReconnect: 0
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\fInheritReconnectSame: 1
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\fReconnectSame: 0
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\fInheritMaxSessionTime: 1
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\fInheritMaxDisconnectionTime: 1
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\MaxDisconnectionTime: 0
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\MaxConnectionTime: 0
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\fInheritMaxIdleTime: 1
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\MaxIdleTime: 0
    HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Winstations\RDP-Tcp\MaxInstanceCount: 4294967295

#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$SubscriptionId,
        [Parameter(Mandatory=$True)]
        [String]$ResourceGroup,
        [Parameter(Mandatory=$True)]
        [String]$Server
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "Content-Type" =  "application/json"
            "x-ms-command-name" = "Microsoft_Azure_Automation."
        }

        # Create the body
        $body="{""commandId"":""RDPSettings""}"

        # Invoke the command
        $response = Invoke-WebRequest -UseBasicParsing -Method Post -Uri "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroup/providers/Microsoft.Compute/virtualMachines/$Server/runCommand?api-version=2018-04-01" -Headers $headers -Body $body

        # Get the async operation url
        $async = $response.Headers["Azure-AsyncOperation"]
        Write-Verbose "Azure-AsyncOperation: $async"

        while($status = Invoke-RestMethod -UseBasicParsing -Uri $async -Headers $headers)
        {
            if($status.status -eq "InProgress")
            {
                # Still pending, wait for two seconds
                Start-Sleep -Seconds 5
            }
            else
            {
                if($status.status -eq "Succeeded")
                {
                    # Script was executed successfully - but we don't the actual result
                    $value = $status.properties.output.value

                    # Loop through the output streams
                    foreach($output in $value)
                    {
                        if($output.code.Contains("StdOut"))
                        {
                            Write-Host $output.message
                        }
                        elseif($output.code.Contains("StdErr") -and -not [string]::IsNullOrEmpty($output.message))
                        {
                            Write-Error $output.message
                        }
                    }

                }
                else
                {
                    Write-Error "The script failed"
                }
                
                break
            }
        }
        
    }
}

# Gets Azure Role Assignment ID for the given name
# Jun 3rd 2020
function Get-AzureRoleAssignmentId
{
<#
    .SYNOPSIS
    Gets Azure Role Assignment ID for the given role name

    .DESCRIPTION
    Gets Azure Role Assignment ID for the given role name

    .Example
    Get-AADIntAzureRoleAssignmentId -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 -RoleName "Virtual Machine Contributor"

    9980e02c-c2be-4d73-94e8-173b1dc7cf3c
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$SubscriptionId,
        [Parameter(Mandatory=$True)]
        [String]$RoleName
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
        }

        # Invoke the command
        $response = Invoke-RestMethod -UseBasicParsing -Method Get -Uri "https://management.azure.com/subscriptions/$SubscriptionId/providers/Microsoft.Authorization/roleDefinitions?`$filter=roleName eq '$RoleName'&api-version=2018-01-01-preview" -Headers $headers

        # Return the ID
        $response.value[0].name
        
    }
}


# Assigns a role to a given user
# Jun 3rd 2020
function Set-AzureRoleAssignment
{
<#
    .SYNOPSIS
    Assigns a role to a given user

    .DESCRIPTION
    Assigns a role to a given user

    .Example
    Set-AADIntAzureRoleAssignment -AccessToken $at -SubscriptionId 867ae413-0ad0-49bf-b4e4-6eb2db1c12a0 -Role Name "Virtual Machine Contributor"

    roleDefinitionId : /subscriptions/867ae413-0ad0-49bf-b4e4-6eb2db1c12a0/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c
    principalId : 90f9ca62-2238-455b-bb15-de695d689c12
    principalType : User
    scope : /subscriptions/867ae413-0ad0-49bf-b4e4-6eb2db1c12a0
    createdOn : 2020-06-03T11:29:58.1683714Z
    updatedOn : 2020-06-03T11:29:58.1683714Z
    createdBy :
    updatedBy : 90f9ca62-2238-455b-bb15-de695d689c12
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$SubscriptionId,
        [Parameter(Mandatory=$False)]
        [String]$UserName,
        [Parameter(Mandatory=$True)]
        [String]$RoleName
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "Content-Type" =  "application/json"
        }

        # Get the role id
        $roleId=Get-AzureRoleAssignmentId -AccessToken $AccessToken -SubscriptionId $SubscriptionId -RoleName $RoleName

        # If user name is not given, use the id from the Access Token
        if([string]::IsNullOrEmpty($UserName))
        {
            $userId = (Read-AccessToken -AccessToken $AccessToken).oid
        }
        else
        {
            # TODO: get the id
            Throw "Not implemented yet. Only current user is supported."
        }

        # Create the body
        $body=@"
        {
          "properties": {
            "roleDefinitionId": "/subscriptions/$SubscriptionId/providers/Microsoft.Authorization/roleDefinitions/$roleId",
            "principalId": "$userId",
            "canDelegate": false
          }
        }

"@


        # Invoke the command
        $response = Invoke-RestMethod -UseBasicParsing -Method Put -Uri "https://management.azure.com/subscriptions/$SubscriptionId/providers/Microsoft.Authorization/roleAssignments/$(New-Guid)?api-version=2018-09-01-preview" -Headers $headers -Body $body

        # Return the results
        $response.properties
    }
}


# Lists azure tenants of the logged in user
# Jun 10th 2020
function Get-AzureTenants
{
<#
    .SYNOPSIS
    Lists all Azure AD tenants the user has access to.

    .DESCRIPTION
    Lists all Azure AD tenants the user has access to.

    .Example
    $at=Get-AADIntAccessTokenForAzureCoreManagement
    PS C:\>Get-AADIntAzureTenants -AccessToken $at

    Id Country Name Domains
    -- ------- ---- -------
    221769d7-0747-467c-a5c1-e387a232c58c FI Firma Oy {firma.mail.onmicrosoft.com, firma.onmicrosoft.com, firma.fi}
    6e3846ee-e8ca-4609-a3ab-f405cfbd02cd US Company Ltd {company.onmicrosoft.com, company.mail.onmicrosoft.com,company.com}

    .Example
    Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache

    Tenant User Resource Client
    ------ ---- -------- ------
    6e3846ee-e8ca-4609-a3ab-f405cfbd02cd https://management.core.windows.net/ d3590ed6-52b3-4102-aeff-aad2292ab01c

    PS C:\>Get-AADIntAzureTenants

    Id Country Name Domains
    -- ------- ---- -------
    221769d7-0747-467c-a5c1-e387a232c58c FI Firma Oy {firma.mail.onmicrosoft.com, firma.onmicrosoft.com, firma.fi}
    6e3846ee-e8ca-4609-a3ab-f405cfbd02cd US Company Ltd {company.onmicrosoft.com, company.mail.onmicrosoft.com,company.com}
   
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "Content-type" = "application/json"
        }

        $body=@"
        {
            "requests":
            [
                {
                    "httpMethod":"GET",
                    "name":"$((New-Guid).ToString())",
                    "requestHeaderDetails":
                    {
                        "commandName":"fx.Services.Tenants.getTenants"
                    },
                    "url":"/tenants?api-version=2019-03-01&`$includeAllTenantCategories=true"
                }
            ]
        }'
"@


        # Invoke the command.
        $response = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://management.azure.com/batch?api-version=2015-11-01" -Headers $headers -Body $body

        # Return
        foreach($value in $response.responses[0].content.value)
        {
            $attributes=[ordered]@{
                "Id" =      $value.tenantId
                #"Type" = $value.tenantCategory # All are "Home"
                "Country" = $value.countryCode
                "Name" =    $value.displayName
                "Domains" = $value.domains
            }
            New-Object psobject -Property $attributes
        }
        
    }
}


# Invokes an Azure query
# Jan 22nd 2021
function Invoke-AzureQuery
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$Query,
        [Parameter(Mandatory=$True)]
        [GUID]$SubscriptionId
    )
    Process
    {
        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "Content-type" = "application/json"
        }

        $body=@"
        {
            "requests": [{
                    "content": {
                        "subscriptions": ["$($SubscriptionId.toString())"],
                        "query": "$Query"
                    },
                    "httpMethod": "POST",
                    "name": "$((New-Guid).toString())",
                    "requestHeaderDetails": {
                        "commandName": "Microsoft_Azure_Security_Insights."
                    },
                    "url": "https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2019-04-01"
                }
            ]
        }
"@


        # Invoke the command.
        $response = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://management.azure.com/batch?api-version=2015-11-01" -Headers $headers -Body $body

        return $response
        
    }
}


# Show diagnostic settings
# Jan 22nd 2021
function Get-AzureDiagnosticSettingsDetails
{
<#
    .SYNOPSIS
    Gets log settings of the given Azure workspace.

    .DESCRIPTION
    Gets log settings of the given Azure workspace.

    .Parameter AccessToken
    AccessToken of the user. Must be Global Administrator or Security Administrator.

    .Parameter Name
    Name of the Sentinel workspace.

    .Example
    Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache
    PS C:\>Get-AADIntDiagnosticSettingsDetails -Name "Audit and SignIn to Sentinel"

    Log Enabled Retention Enabled Retention Days
    --- ------- ----------------- --------------
    ProvisioningLogs False False 0
    AuditLogs True True 365
    SignInLogs True True 365
    NonInteractiveUserSignInLogs False False 0
    ServicePrincipalSignInLogs False False 0
    ManagedIdentitySignInLogs True True 365
    ADFSSignInLogs False False 365

    .Example
    $at=Get-AADIntAccessTokenForAzureCoreManagement

    PS C:\>Get-AADIntAzureDiagnosticSettings

    Name : Audit and SignIn to Sentinel
    WorkspaceId : /subscriptions/a04293e7-46c8-4bf4-bc6d-1bc1f41afae0/resourcegroups/sentinel/providers/microsoft.operationalinsights/workspaces/MySentinel
    StorageAccountId :
    EventHubAuthorizationRuleId :
    EventHubName :
    ServiceBusRuleId :

    Name : Service Principal to Sentinel
    WorkspaceId : /subscriptions/a04293e7-46c8-4bf4-bc6d-1bc1f41afae0/resourcegroups/sentinel/providers/microsoft.operationalinsights/workspaces/MySentinel
    StorageAccountId :
    EventHubAuthorizationRuleId :
    EventHubName :
    ServiceBusRuleId :

    PS C:\>Get-AADIntDiagnosticSettingsDetails -Name "Audit and SignIn to Sentinel"

    Log Enabled Retention Enabled Retention Days
    --- ------- ----------------- --------------
    ProvisioningLogs False False 0
    AuditLogs True True 365
    SignInLogs True True 365
    NonInteractiveUserSignInLogs False False 0
    ServicePrincipalSignInLogs False False 0
    ManagedIdentitySignInLogs True True 365
    ADFSSignInLogs False False 0

#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$Name
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "Content-type" = "application/json"
        }
        
        # Invoke the command.
        $response = Invoke-RestMethod -UseBasicParsing -Method Get -Uri "https://management.azure.com/providers/microsoft.aadiam/diagnosticSettings/$Name`?api-version=2017-04-01" -Headers $headers

        
        # Return
        foreach($value in $response.properties.logs)
        {
            $attributes=[ordered]@{
                "Log" =               $value.category
                "Enabled" =           $value.enabled
                "Retention Enabled" = $value.retentionPolicy.enabled
                "Retention Days" =    $value.retentionPolicy.days
            }
            New-Object psobject -Property $attributes
        }
    }
}


# Set diagnostic settings
# Jan 22nd 2021
function Set-AzureDiagnosticSettingsDetails
{
<#
    .SYNOPSIS
    Sets log settings for the given Azure Workspace.

    .DESCRIPTION
    Sets log settings for the given Azure Workspace.

    .Parameter AccessToken
    AccessToken of the user. Must be Global Administrator or Security Administrator.

    .Parameter Name
    Name of the Sentinel workspace.

    .Parameter Logs
    List of logs to be edited, can be one or more of "AuditLogs","SignInLogs","NonInteractiveUserSignInLogs","ServicePrincipalSignInLogs","ManagedIdentitySignInLogs","ProvisioningLogs","ADFSSignInLogs","RiskyUsers","UserRiskEvents".
    
    .Parameter Enabled
    Is the log enabled.

    .Parameter RetentionEnabled
    Is the log retention enabled.

    .Parameter RetentionDays
    The number of retention days. Must be between 0 and 365 days.

    .Example
    Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache

    PS C:\>Set-AADIntDiagnosticSettingsDetails -Name "Audit and SignIn to Sentinel" -Log ManagedIdentitySignInLogs,AuditLogs,SignInLogs -Enabled $true -RetentionEnabled $true -RetentionDays 365

    Log Enabled Retention Enabled Retention Days
    --- ------- ----------------- --------------
    ProvisioningLogs False False 0
    AuditLogs True True 365
    SignInLogs True True 365
    NonInteractiveUserSignInLogs False False 0
    ServicePrincipalSignInLogs False False 0
    ManagedIdentitySignInLogs True True 365
    ADFSSignInLogs False False 0
    RiskyUsers False False 0
    UserRiskEvents False False 0

    .Example
    $at=Get-AADIntAccessTokenForAzureCoreManagement

    PS C:\>Get-AADIntAzureDiagnosticSettings

    Name : Audit and SignIn to Sentinel
    WorkspaceId : /subscriptions/a04293e7-46c8-4bf4-bc6d-1bc1f41afae0/resourcegroups/sentinel/providers/microsoft.operationalinsights/workspaces/MySentinel
    StorageAccountId :
    EventHubAuthorizationRuleId :
    EventHubName :
    ServiceBusRuleId :

    Name : Service Principal to Sentinel
    WorkspaceId : /subscriptions/a04293e7-46c8-4bf4-bc6d-1bc1f41afae0/resourcegroups/sentinel/providers/microsoft.operationalinsights/workspaces/MySentinel
    StorageAccountId :
    EventHubAuthorizationRuleId :
    EventHubName :
    ServiceBusRuleId :

    PS C:\>Set-AADIntDiagnosticSettingsDetails -Name "Audit and SignIn to Sentinel" -Log ManagedIdentitySignInLogs,AuditLogs,SignInLogs -Enabled $true -RetentionEnabled $true -RetentionDays 365

    Log Enabled Retention Enabled Retention Days
    --- ------- ----------------- --------------
    ProvisioningLogs False False 0
    AuditLogs True True 365
    SignInLogs True True 365
    NonInteractiveUserSignInLogs False False 0
    ServicePrincipalSignInLogs False False 0
    ManagedIdentitySignInLogs True True 365

#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [ValidateSet("AuditLogs","SignInLogs","NonInteractiveUserSignInLogs","ServicePrincipalSignInLogs","ManagedIdentitySignInLogs","ProvisioningLogs","ADFSSignInLogs","RiskyUsers","UserRiskEvents")]
        [String[]]$Logs,
        [Parameter(Mandatory=$True)]
        [String]$Name,
        [Parameter(Mandatory=$True)]
        [bool]$Enabled,
        [Parameter(Mandatory=$True)]
        [bool]$RetentionEnabled,
        [Parameter(Mandatory=$True)]
        [int]$RetentionDays

    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Check the retention days value
        if($RetentionDays -lt 0 -or $RetentionDays -gt 365)
        {
            Write-Error "Retention days must be between 0 and 365 days"
            return
        }

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "Content-type" = "application/json"
        }

        # Get the current settings and workspaceid
        $response = Invoke-RestMethod -UseBasicParsing -Method Get -Uri "https://management.azure.com/providers/microsoft.aadiam/diagnosticSettings/$Name`?api-version=2017-04-01" -Headers $headers
        $workspaceId = $response.properties.workspaceId

        # Create the array for log settings objects
        $log_array = @()
        foreach($value in $response.properties.logs)
        {
            # Check if this log is to be changed
            if($logs -contains $value.category)
            {
                $log_entry = @{
                    "category" = $value.category
                    "enabled" =  $Enabled
                    "retentionPolicy" = @{
                        "days" =    $RetentionDays
                        "enabled" = $RetentionEnabled
                        }
                    }
                
            }
            else
            {
                $log_entry = @{
                    "category" = $value.category
                    "enabled" =  $value.enabled
                    "retentionPolicy" = @{
                        "days" =    $value.retentionPolicy.days
                        "enabled" = $value.retentionPolicy.enabled
                        }
                    }
            }
            $log_array += $log_entry
        }

        # Create the body
        $body = @{
            "id" =   "/providers/microsoft.aadiam/diagnosticSettings/$Name"
            "name" = $Name
            "properties" = @{
                "logs" =        $log_array
                "metrics" =     @()
                "workspaceId" = $WorkspaceId
                }
        }
        
        # Invoke the command.
        $response = Invoke-RestMethod -UseBasicParsing -Method Put -Uri "https://management.azure.com/providers/microsoft.aadiam/diagnosticSettings/$Name`?api-version=2017-04-01" -Headers $headers -Body ($body | ConvertTo-Json -Depth 5)

        
        # Return
        foreach($value in $response.properties.logs)
        {
            $attributes=[ordered]@{
                "Log" =               $value.category
                "Enabled" =           $value.enabled
                "Retention Enabled" = $value.retentionPolicy.enabled
                "Retention Days" =    $value.retentionPolicy.days
            }
            New-Object psobject -Property $attributes
        }


        
    }
}

# List diagnostic settings
# Jan 22nd 2021
function Get-AzureDiagnosticSettings
{
<#
    .SYNOPSIS
    Lists all diagnostic settings.

    .DESCRIPTION
    Lists all diagnostic settings.

    .Parameter AccessToken
    AccessToken of the user. Must be Global Administrator or Security Administrator.

    .Example
    Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache
    PS C:\>Get-AADIntAzureDiagnosticSettings

    Name : Audit and SignIn to Sentinel
    WorkspaceId : /subscriptions/a04293e7-46c8-4bf4-bc6d-1bc1f41afae0/resourcegroups/sentinel/providers/microsoft.operationalinsights/workspaces/MySentinel
    StorageAccountId :
    EventHubAuthorizationRuleId :
    EventHubName :
    ServiceBusRuleId :

    Name : Service Principal to Sentinel
    WorkspaceId : /subscriptions/a04293e7-46c8-4bf4-bc6d-1bc1f41afae0/resourcegroups/sentinel/providers/microsoft.operationalinsights/workspaces/MySentinel
    StorageAccountId :
    EventHubAuthorizationRuleId :
    EventHubName :
    ServiceBusRuleId :


   
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Set the headers
        $headers=@{
            "Authorization" =     "Bearer $AccessToken"
            "Content-type" =      "application/json"
            "x-ms-command-name" = "Microsoft_Azure_Monitoring"
            "x-ms-path-query" =   "/providers/microsoft.aadiam/diagnosticSettings?api-version=2017-04-01-preview"
        }


        $response = Invoke-RestMethod -UseBasicParsing -Method Get -Uri "https://management.azure.com/api/invoke" -Headers $headers

        # Return
        foreach($value in $response.value)
        {
            $attributes=[ordered]@{
                "Name" =                        $value.name
                "WorkspaceId" =                 $value.properties.workspaceId
                "StorageAccountId" =            $value.properties.storageAccountId
                "EventHubAuthorizationRuleId" = $value.properties.eventHubAuthorizationRuleId
                "EventHubName" =                $value.properties.eventHubName
                "ServiceBusRuleId" =            $value.properties.serviceBusRuleId
            }
            New-Object psobject -Property $attributes
        }
        
    }
}

# Remove all diagnostic settings
# Jan 23rd 2021
function Remove-AzureDiagnosticSettings
{
<#
    .SYNOPSIS
    Removes all diagnostic settings.

    .DESCRIPTION
    Removes all diagnostic settings by disabling all logs.

    .Parameter AccessToken
    AccessToken of the user. Must be Global Administrator or Security Administrator.

    .Example
    Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache
    PS C:\>Remove-AADIntAzureDiagnosticSettings
  
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$False)]
        [Switch]$Force
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        # Get the list of diagnostic settings
        $diagSettings = Get-AzureDiagnosticSettings -AccessToken $AccessToken

        $count = $diagSettings.count

        if(!$count)
        {
            $count = 1
        }

        if(!$Force)
        {
            $answer = Read-Host -Prompt "About to delete $count diagnostic settings. Are you sure? (Y/N)"
            if($answer -ne "Y")
            {
                return
            }
        }

        foreach($settings in $diagSettings)
        {
            Write-Verbose "Removing diagnostic settings ""$($settings.name)"""
            Set-AzureDiagnosticSettingsDetails -AccessToken $AccessToken -Name $($settings.name) -Logs "AuditLogs","SignInLogs","NonInteractiveUserSignInLogs","ServicePrincipalSignInLogs","ManagedIdentitySignInLogs","ProvisioningLogs","ADFSSignInLogs","RiskyUsers","UserRiskEvents" -Enabled $False -RetentionEnabled $False -RetentionDays 0 | Out-Null
        }
        
    }
}

# Get Azure Directory Activity Log
# Aug 8th 2021
function Get-AzureDirectoryActivityLog
{
<#
    .SYNOPSIS
    Gets Azure Directory Activity log events.

    .DESCRIPTION
    Gets Azure Directory Activity log events even from tenants without Azure subscription.

    .Parameter AccessToken
    AccessToken of the user. Should be Global Administrator with access to any Azure subscription of the tenant.
    If the tenant doesn't have Azure subscription, the user must have "Access management for Azure resources"
    switched on at https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Properties or use
    Grant-AADIntAzureUserAccessAdminRole

    .Parameter Start
    Start time, must be 90 days of less of the current time. Defaults to one day.

    Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache
    PS C:\>Grant-AADIntAzureUserAccessAdminRole
    PS C:\>$events = Get-AADIntAzureDirectoryActivityLog -Start (Get-Date).AddDays(-31)
    PS C:\>$events | where {$_.authorization.action -like "Microsoft.ADHybrid*"} | %{New-Object psobject -Property ([ordered]@{"Scope"=$_.authorization.scope;"Operation"=$_.operationName.localizedValue;"Caller"=$_.caller;"TimeStamp"=$_.eventTimeStamp})}

    Scope Operation Caller
    ----- --------- ------
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Creates a server. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Creates a server. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Creates a server. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Creates a server. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Creates a server. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Creates a server. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Creates a server. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Creates a server. administrator...
    /providers/Microsoft.ADHybridHealthService Updates a service. administrator...
    /providers/Microsoft.ADHybridHealthService Updates a service. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Deletes service. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Deletes service. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Deletes service. administrator...
    /providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Deletes service. administrator...
    /providers/Microsoft.ADHybridHealthService Updates a service. administrator...
    /providers/Microsoft.ADHybridHealthService Updates a service. administrator...
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$False)]
        [DateTime]$Start=(Get-Date).AddDays(-1)
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://management.core.windows.net/" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"

        $headers = @{
            "Authorization" = "Bearer $AccessToken"
        }

        $startTime = $Start.ToUniversalTime().ToString("s", [cultureinfo]::InvariantCulture)+"Z"

        $response = Invoke-RestMethod -UseBasicParsing -Uri "https://management.azure.com/providers/microsoft.insights/eventtypes/management/values?api-version=2015-04-01&`$filter=eventTimestamp ge '$startTime'" -Headers $headers
        while($response.nextLink)
        {
            $response.value
            $response = Invoke-RestMethod -Uri $response.nextLink -Headers $headers
        }
        $response.value
       

    }
}