AzureManagementAPI_utils.ps1



# Creates a web session with given cookie header
function Create-WebSession
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [string]$SetCookieHeader,
        [Parameter(Mandatory=$True)]
        [string]$Domain
    )
    Process 
    {
        
        
        $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession

        # login.live.com: MSPRequ=lt=1540361812&co=1&id=N; secure= ;path=/;HTTPOnly=;version=1,uaid=bf4b7f37ec1d4084aa68952b9edebb6b; domain=login.live.com;secure= ;path=/;HTTPOnly= ;version=1,MSPOK=$uuid-f4f5ef25-7343-4de8-84cb-266ea1b47bc2; domain=login.live.com;secure= ;path=/;HTTPOnly= ;version=1
        if($domain -eq "login.live.com")
        {
            $SetCookie = $SetCookieHeader.Split(";,")
            foreach($Cookie in $SetCookie) 
            {
                $name = $Cookie.Split("=")[0].trim()
                $value = $Cookie.Substring($name.Length+1)
                switch($name)
                {
                    "secure" {}
                    "path" {}
                    "HTTPOnly" {}
                    "domain" {}
                    "version" {}
                    default 
                    {
                        $webCookie = New-Object System.Net.Cookie
                        $webCookie.Name = $name
                        $webCookie.Value = $value
                        $webCookie.Domain = $Domain
                        $session.Cookies.Add($webCookie)
                        Write-Verbose "COOKIE [$Domain]: $webCookie"
                    }
                }
            }
            
        }
        elseif($domain.EndsWith(".sharepoint.com")) # Sharepoint
        {
            $SetCookie = $SetCookieHeader.Replace("HttpOnly","|").Split("|")
            foreach($Cookie in $SetCookie) 
            {
                if(![String]::IsNullOrEmpty($Cookie))
                {
                    $Cookie = $Cookie.Split(";")[0].trim()
                    $name = $Cookie.Split("=")[0].trim()
                    $value = $Cookie.Substring($name.Length+1)

                    # Strip the trailing semi colon
                    $value=$value.Split(";")[0]
                
                    $webCookie = New-Object System.Net.Cookie
                    $webCookie.Name = $name
                    $webCookie.Value = $value
                    $webCookie.Domain = $Domain
                    $session.Cookies.Add($webCookie)
                    Write-Verbose "COOKIE [$Domain]: $webCookie"
                }
            }
        }
        else # login.microsoftonline.com:
        {
            # Split the cookie string
            $SetCookie = $SetCookieHeader.Replace("HttpOnly","|").Split("|")
            foreach($Cookie in $SetCookie) 
            {
                # Split the individual cookie and remove possible trailing comma
                $Cookie=($Cookie.Split(";")[0]).Replace(',','')
                if(![string]::IsNullOrEmpty($Cookie))
                {
                    $webCookie = New-Object System.Net.Cookie
                    $webCookie.Name = $Cookie.Split("=")[0]
                    $webCookie.Value = $Cookie.Substring($webCookie.Name.Length+1)
                    $webCookie.Domain = $Domain
                    $session.Cookies.Add($webCookie)
                    Write-Verbose "COOKIE [$Domain]: $webCookie"
                }
            
            }
        }
        return $session
    }
}

# Creates a web session with given cookie header
function Create-WebSession2
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [System.Security.]$Headers,
        [Parameter(Mandatory=$True)]
        [string]$Domain
    )
    Process
    {
        $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession

        # Split the cookie string
        $SetCookie = $SetCookieHeader
        $SetCookie = $SetCookie.Replace("HttpOnly","|").Replace("HTTPOnly","|").Split("|")
        foreach($Cookie in $SetCookie) 
        {
            # Split the individual cookie and remove possible trailing comma
            $Cookie=($Cookie.Split(";")[0]).Replace(',','')
            if(![string]::IsNullOrEmpty($Cookie))
            {
                $webCookie = New-Object System.Net.Cookie
                $webCookie.Name = $Cookie.Split("=")[0]
                $webCookie.Value = $Cookie.Split("=")[1]
                $webCookie.Domain = $Domain
                $session.Cookies.Add($webCookie)
            }
            
        }
        return $session
    }
}

# Gets the access token for Azure Management API
# Uses totally different flow than other access tokens.
# Oct 23rd 2018
# TODO: Add support for form based & SAML authentication
function Get-AccessTokenForAzureMgmtAPI
{
<#
    .SYNOPSIS
    Gets OAuth Access Token for Azure Management API
 
    .DESCRIPTION
    Gets OAuth Access Token for Azure Management API
 
    .Parameter Credentials
    Credentials of the user.
     
    .Example
    PS C:\>$cred=Get-Credential
    PS C:\>Get-AADIntAccessTokenForAzureMgmtAPI -Credentials $cred
#>

    [cmdletbinding()]
    Param(
        [Parameter()]
        [System.Management.Automation.PSCredential]$Credentials,
        [Parameter()]
        [Switch]$ExportTokenObject
    )
    Process
    {
        $accessToken=""
        # Check if we got credentials
        if([string]::IsNullOrEmpty($Credentials) -and [string]::IsNullOrEmpty($SAMLToken))
        {
            # No credentials given, so prompt for credentials
            $accessToken = Prompt-AzureADCredentials
                
        }
        else
        {
            $userName = $Credentials.UserName
            $password = $credentials.GetNetworkCredential().Password


            # Step 1: Go to portal.azure.com to get cookies and authentication url
            $response = Invoke-WebRequest -uri "https://portal.azure.com/signin/idpRedirect.js/?feature.settingsportalinstance=&idpRedirectCount=0."
            $html=$response.Content
            $s=$html.IndexOf('https://login.microsoftonline.com')
            $e=$html.IndexOf('"',$s)
            $url=$html.Substring($s,$e-$s)
            $azureWebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "portal.azure.com"

            # Step 2: Go to login.microsoftonline.com to get configuration and cookies
            $response = Invoke-WebRequest -uri $url -Headers @{Cookie="x-ms-gateway-slice=004; stsservicecookie=ests; AADSSO=NANoExtension; SSOCOOKIEPULLED=1"}
            $html = $response.Content

            $s=$html.IndexOf('$Config=')
            $e=$html.IndexOf('};',$s+8)
            $config=$html.Substring($s+8,$e-$s-7) | ConvertFrom-Json
            $MSOnlineComwebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "login.microsoftonline.com"

            # Step3: Get user information, including Flow Token
            $userInfo=Get-CredentialType -UserName $userName -FlowToken $config.sFT

            # LOGIN.LIVE.COM
            if($userInfo.EstsProperties.DomainType -eq 2) # =live account
            {
                # Step L1: Go to login.live.com to get configuration and cookies
                $response = Invoke-WebRequest -uri $config.urlGoToAADError
                $html = $response.Content

                $s=$html.IndexOf('ServerData =')
                $e=$html.IndexOf('};',$s+13)
                $config=$html.Substring($s+13,$e-$s-12) 
            
                # ConvertFrom-Json is caseinsensitive so need to use this one
                $config = (New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property @{MaxJsonLength=67108864}).DeserializeObject($config)

                $liveWebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "login.live.com"

                $sFTTag= [xml]$config.sFTTag
                $PPFT = $sFTTag.SelectSingleNode("//input[@name='PPFT']").value
            

                # Step L2: Login to login.live.com
                $body=@{
                    "login" = $userName
                    "loginFmt" = $userName
                    "i13"="0"
                    "type"="11"
                    "LoginOptions"="3"
                    "passwd"=$password
                    "ps"="2"
                    "canary"=""
                    "ctx"=""
                    "NewUser"="1"
                    "fspost"="0"
                    "i21"="0"
                    "CookieDisclosure"="1"
                    "IsFidoSupported"="1"
                    "hpgrequestid"=""
                    "PPSX"="Pa"
                    "PPFT"=$PPFT
                    "i18"="__ConvergedLoginPaginatedStrings|1,__OldConvergedLogin_PCore|1,"
                    "i2"="1"
                    }
                $headers=@{
                    "User-Agent"="Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
                    "Upgrade-Insecure-Requests" = "1"

                }

                $response = Invoke-WebRequest -Uri $config.urlPost -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -Headers $headers -WebSession $liveWebSession
                $html = $response.Content

                # No well formed xml, so need to do some tricks.. First, find the form start and end tags
                $s=$html.IndexOf("<form")
                $e=$html.IndexOf("</form>")
                $form = $html.Substring($s,$e-$s) # Strip the form end tag
                # End all tags
                $form=$form.replace('">','"/>')
                # Add start and end tags
                $form="<html>$form</html>"

                $html=[xml]$form

                $fmHF = $html.SelectSingleNode("//form[@name='fmHF']").action
                $code = $html.SelectSingleNode("//input[@name='code']").value
                $state = $html.SelectSingleNode("//input[@name='state']").value


                # Step L3: Login to login.microsoftonline.com with code and state
                $body = @{
                    "code" = $code
                    "state" = $state
                }
                $response = Invoke-WebRequest -Uri $fmHF -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -Headers $headers -WebSession $MSOnlineComwebSession
                $MSOnlineComwebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "login.microsoftonline.com"
                $html = $response.Content
                $s=$html.IndexOf('$Config=')
                $e=$html.IndexOf('};',$s+8)
                $config=$html.Substring($s+8,$e-$s-7) | ConvertFrom-Json
            

                # Step L4: Get code, id_token, and state information
                $body=@{
                    "LoginOptions"="0"
                    "flowToken"=$config.sFT
                    "canary"=$config.canary
                    "ctx"=$config.sCtx
                    "hpgrequestid"=(New-Guid).ToString()
                }
                $response = Invoke-WebRequest -Uri "https://login.microsoftonline.com/kmsi" -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -Headers $headers -WebSession $MSOnlineComwebSession
                $MSOnlineComwebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "login.microsoftonline.com"
                $html = [xml]$response.Content
                $code = $html.SelectSingleNode("//input[@name='code']").value
                $id_token = $html.SelectSingleNode("//input[@name='id_token']").value
                $state = $html.SelectSingleNode("//input[@name='state']").value
                $session_state = $html.SelectSingleNode("//input[@name='session_state']").value

                # Step L5: Sign in to portal.azure.com to get redirect URL
                $body=@{
                    "code"= $code
                    "id_token" = $id_token
                    "state" = $state
                    "session_state" = $session_state
                }
                $response = Invoke-WebRequest -Uri "https://portal.azure.com/signin/index/" -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -Headers $headers -WebSession $azureWebSession
                $azureWebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "portal.azure.com"
                $html=$response.Content
                $s=$html.IndexOf('MsPortalImpl.redirectToUri("')
                $e=$html.IndexOf('")',$s)
                $url=$html.Substring($s+28,$e-$s-28) 

                # Step L6: Go to portal.azure.com to get another redirect URL
                $response = Invoke-WebRequest -Uri $url -Method Get -Headers $headers -WebSession $azureWebSession
                $azureWebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "portal.azure.com"
                $html=$response.Content
                $s=$html.IndexOf('https://login.microsoftonline.com')
                $e=$html.IndexOf('"',$s)
                $url=$html.Substring($s,$e-$s)# |ConvertFrom-Json

                # Step L7: Login to login.microsoftonline.com (again) using the received url to get code etc.
                $response = Invoke-WebRequest -Uri $url -Method Get -ContentType "application/x-www-form-urlencoded" -Headers $headers -WebSession $MSOnlineComwebSession
                $MSOnlineComwebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "login.microsoftonline.com"
                $html = [xml]$response.Content
                $code = $html.SelectSingleNode("//input[@name='code']").value
                $id_token = $html.SelectSingleNode("//input[@name='id_token']").value
                $state = $html.SelectSingleNode("//input[@name='state']").value
                $session_state = $html.SelectSingleNode("//input[@name='session_state']").value
                $url = $html.SelectSingleNode("//form[@name='hiddenform']").action

                # Step L8: Sign in to portal.azure.com to get OAuth token
                $body=@{
                    "code"= $code
                    "id_token" = $id_token
                    "state" = $state
                    "session_state" = $session_state
                }
                $response = Invoke-WebRequest -Uri $url -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -Headers $headers -WebSession $azureWebSession
                $azureWebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "portal.azure.com"
            
            }
            else # LOGIN.MICROSOFTONLINE.COM
            {

                # Step M1: Login to login.microsoftonline.com
                $body=@{
                    "login" = $userName
                    "loginFmt" = $userName
                    "i13"="0"
                    "type"="11"
                    "LoginOptions"="3"
                    "passwd"=$password
                    "ps"="2"
                    "flowToken"=$userInfo.FlowToken
                    "canary"=$config.canary
                    "ctx"=$config.sCtx
                    "NewUser"="1"
                    "fspost"="0"
                    "i21"="0"
                    "CookieDisclosure"="1"
                    "IsFidoSupported"="1"
                    "hpgrequestid"=(New-Guid).ToString()
                }
                $headers=@{
                    "User-Agent"="Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
                    "Upgrade-Insecure-Requests" = "1"

                }
                $response = Invoke-WebRequest -Uri "https://login.microsoftonline.com/common/login" -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -Headers $headers -WebSession $MSOnlineComwebSession
                $MSOnlineComwebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "login.microsoftonline.com"
                $html = $response.Content
                $s=$html.IndexOf('$Config=')
                $e=$html.IndexOf('};',$s+8)
                $config=$html.Substring($s+8,$e-$s-7) | ConvertFrom-Json
                $MSOnlineComwebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "login.microsoftonline.com"

                # Step M2: Get code, id_token, and state information
                $body=@{
                    "LoginOptions"="0"
                    "flowToken"=$config.sFT
                    "canary"=$config.canary
                    "ctx"=$config.sCtx
                    "hpgrequestid"=(New-Guid).ToString()
                }
                $response = Invoke-WebRequest -Uri "https://login.microsoftonline.com/kmsi" -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -Headers $headers -WebSession $MSOnlineComwebSession
                $MSOnlineComwebSession = Create-WebSession -SetCookieHeader $response.Headers.'Set-Cookie' -Domain "login.microsoftonline.com"
                $html = [xml]$response.Content
                $code = $html.SelectSingleNode("//input[@name='code']").value
                $id_token = $html.SelectSingleNode("//input[@name='id_token']").value
                $state = $html.SelectSingleNode("//input[@name='state']").value
                $session_state = $html.SelectSingleNode("//input[@name='session_state']").value

                # Step M3: Sign in to portal.azure.com
                $body=@{
                    "code"= $code
                    "id_token" = $id_token
                    "state" = $state
                    "session_state" = $session_state
                }
                $response = Invoke-WebRequest -Uri "https://portal.azure.com/signin/index/" -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -Headers $headers -WebSession $azureWebSession
            }

            # Get the OAuth token
            $html=$response.Content

            $s=$html.IndexOf('{"oAuthToken":')
            $e=$html.IndexOf('}}',$s)
            $token=$html.Substring($s,$e-$s+2) |ConvertFrom-Json

            # Return
            $accessToken = $token.oAuthToken.authHeader.Split(" ")[1]
            $refreshToken = $token.oAuthToken.refreshToken

        }

        # Save the tokens to cache
        $tokenToCache = New-Object PSObject -Property @{"access_token" = $accessToken; "refresh_token"=$refreshToken}
        $Script:tokens["azureportal"]=$tokenToCache

        # Return
        if($ExportTokenObject)
        {
            return $tokenToCache
        }
        else
        {
            $accessToken
        }
    
    }
}

# Gets the access token for Azure AD IAM API
# Oct 24th 2018
# TODO: Add support for form based & SAML authentication
function Get-AccessTokenForAADIAMAPI
{
<#
    .SYNOPSIS
    Gets OAuth Access Token for Azure AD IAM API
 
    .DESCRIPTION
    Gets OAuth Access Token for Azure AD IAM API
 
    .Parameter Credentials
    Credentials of the user.
     
    .Example
    PS C:\>$cred=Get-Credential
    PS C:\>Get-AADIntAccessTokenForAADIAMAPI -Credentials $cred
#>

    [cmdletbinding()]
    Param(
        [Parameter()]
        [System.Management.Automation.PSCredential]$Credentials
    )
    Process
    {
        $AADAuth = Get-AccessTokenForAzureMgmtAPI -Credentials $Credentials -ExportTokenObject
        $token = Get-DelegationToken -ExtensionName Microsoft_AAD_IAM -AccessToken $AADAuth
        return $token.authHeader.Split(" ")[1]
    }
}

# Get delegation token for the given extension
function Get-DelegationToken
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [ValidateSet('Microsoft_AAD_IAM')]
        [String]$ExtensionName,
        [Parameter(Mandatory=$False)]
        $ResourceName="self",
        [Parameter(Mandatory=$True)]
        $AccessToken
    )
    Process
    {
        # Check the expiration
        if(Is-AccessTokenExpired($AccessToken.access_token))
        {
            throw "AccessToken has expired"
        }


        $Headers=@{
            "x-ms-client-request-id" = (New-Guid).ToString()
            "x-ms-extension-flags" ='{"feature.advisornotificationdays":"30","feature.advisornotificationpercent":"100","feature.armtenants":"true","feature.artbrowse":"true","feature.azureconsole":"true","feature.checksdkversion":"true","feature.contactinfo":"true","feature.dashboardfilters":"false","feature.enableappinsightsmetricsblade":"true","feature.globalsearch":"true","feature.guidedtour":"true","feature.helpcontentenabled":"true","feature.helpcontentwhatsnewenabled":"true","feature.internalonly":"false","feature.irissurfacename":"AzurePortal_Notifications_PROD","feature.mergecoadmins":"true","feature.metricsv2ga":"true","feature.newsubsapi":"true","feature.npsintervaldays":"90","feature.npspercent":"3.0","feature.npsshowportaluri":"true","feature.sessionvalidity":"true","feature.searchnocache":"true","feature.subscreditcheck":"true","hubsextension_parameterseditor":"true","hubsextension_showpolicyhub":"true","feature.autosettings":"true","feature.azurehealth":"true","feature.blockbladeredirect":"Microsoft_Azure_Resources","feature.browsecuration":"default","feature.collapseblade":"true","feature.dashboardfiltersaddbutton":"false","feature.decouplesubs":"true","feature.disablebladecustomization":"true","feature.disabledextensionredirects":"","feature.enablee2emonitoring":"true","feature.enablemonitoringgroup":"true","feature.enableworkbooks":"true","feature.feedback":"true","feature.feedbackwithsupport":"true","feature.fullwidth":"true","feature.managevminbrowse":"true","feature.mgsubs":"true","feature.newautoscale":"true","feature.newtageditorblade":"true","feature.nps":"true","feature.pinnable_default_off":"true","feature.reservationsinbrowse":"true","feature.reservehozscroll":"true","feature.resourcehealth":"true","feature.seetemplate":"true","feature.showdecoupleinfobox":"true","feature.tokencaching":"true","feature.usealertsv2blade":"true","feature.usemdmforsql":"true","feature.usesimpleavatarmenu":"true","hubsextension_budgets":"true","hubsextension_costalerts":"false","hubsextension_costanalysis":"true","hubsextension_costrecommendations":"true","hubsextension_eventgrid":"true","hubsextension_isinsightsextensionavailable":"true","hubsextension_islogsbladeavailable":"true","hubsextension_isomsextensionavailable":"true","hubsextension_savetotemplatehub":"true","hubsextension_servicenotificationsblade":"true","hubsextension_showservicehealthevents":"true","microsoft_azure_marketplace_itemhidekey":"Citrix_XenDesktop_EssentialsHidden,Citrix_XenApp_EssentialsHidden,AzureProject"}'
            "x-ms-version" = "5.0.302.5601 (production#c19533145d.181011-0133) Signed"
            "X-Requested-With" = "XMLHttpRequest"
            "Referer" = "https://portal.azure.com/"
            "x-ms-client-session-id" = $Script:AADSessionId
            "Origin" = "https://portal.azure.com"
            "x-ms-effective-locale"="en.en-us"
            "Accept-Language" = "en"
            "User-Agent"="Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
            "Cookie" ="browserId=6d84502d-b03c-433c-acec-d87e20449090"
        }

        $Body=@{
            "extensionName" = $ExtensionName
            "portalAuthorization" = $AccessToken.refresh_token
            "resourceName" = $ResourceName
            "tenant" = Get-TenantId -AccessToken $AccessToken
        }
        # Call the API
        $response = Invoke-RestMethod -Uri "https://portal.azure.com/api/DelegationToken?feature.tokencaching=true" -ContentType "application/json" -Method POST -Body ($Body | ConvertTo-Json) #-Headers $Headers -WebSession $Script:azureWebSession

        # Return
        $response.value
    }
}

# Calls the Azure AD IAM API
function Call-AzureAADIAMAPI
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        $Body,
        [Parameter(Mandatory=$True)]
        $AccessToken,
        [Parameter(Mandatory=$True)]
        $Command,
        [Parameter(Mandatory=$False)]
        [ValidateSet('Put','Get','Post','Delete')]
        [String]$Method="Get"
    )
    Process
    {
        # Check the expiration
        if(Is-AccessTokenExpired($AccessToken))
        {
            throw "AccessToken has expired"
        }

        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "X-Requested-With" = "XMLHttpRequest"
            "x-ms-client-request-id" = (New-Guid).ToString()
        }
        # Call the API
        $response = Invoke-RestMethod -Uri "https://main.iam.ad.ext.azure.com/api/$command" -ContentType "application/json; charset=utf-8" -Headers $headers -Method $Method -Body ($Body | ConvertTo-Json)

        # Return
        if($response.StatusCode -eq $null)
        {
            return $response
        }
    }
}

# Calls the Azure Management API
# Jul 11th 2019
function Call-AzureManagementAPI
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        $Body,
        [Parameter(Mandatory=$True)]
        $AccessToken,
        [Parameter(Mandatory=$True)]
        $Command
    )
    Process
    {
        # Check the expiration
        if(Is-AccessTokenExpired($AccessToken))
        {
            throw "AccessToken has expired"
        }

        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "X-Requested-With" = "XMLHttpRequest"
            "x-ms-client-request-id" = (New-Guid).ToString()
        }
        # Call the API
        $response=Invoke-RestMethod -Uri "https://portal.azure.com/api/$command" -Method Post -Headers $headers
    
        # Return
        return $response
       
    }
}



# Prompts for credentials and gets the access token
# Supports MFA, federation, etc.
# Jul 11th 2019
function Prompt-AzureADCredentials
{
    [cmdletbinding()]
    Param(
    )
    Process
    {
        # Set variables
        $auth_redirect="https://portal.azure.com/signin/index/"
        $url="https://portal.azure.com/"

        # Create the form
        $form = Create-LoginForm -Url $url -auth_redirect $auth_redirect

        # Show the form and wait for the return value
        if($form.ShowDialog() -ne "OK") {
            # Dispose the control
            $form.Controls[0].Dispose()
            Write-Verbose "Login cancelled"
            return $null
        }

        # Dispose the control
        $form.Controls[0].Dispose()

        # Get the access token from script scope variable
        $accessToken = $script:accessToken
        $script:accessToken = $null

        # Return
        $accessToken
    }
}