Revo.Az.psm1

function New-RevoAzAccess{
    param(
        [parameter(Mandatory=$true, ParameterSetName = "ServicePrincipal")]
        [parameter(Mandatory=$false, ParameterSetName = "UserAuthentication")]
        [string]$TenantID,
        [parameter(Mandatory=$true, ParameterSetName = "ServicePrincipal")]
        [string]$ClientID,
        [parameter(Mandatory=$true, ParameterSetName = "ServicePrincipal")]
        [string]$CertificateThumbprint,
        [parameter(Mandatory=$false, ParameterSetName = "UserAuthentication")]
        [switch]$DeviceAuthentication,
        [parameter(Mandatory=$true, ParameterSetName = "ServicePrincipal")]
        [ValidateSet('CurrentUser','LocalMachine')]
        [String]$CertificateStore = 'CurrentUser',
        [parameter(Mandatory=$false, ParameterSetName = "ServicePrincipal")]
        [switch]$SecureOutput
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{

        if ($CertificateThumbprint) {
            if ($null -eq $env:windir -and $CertificateStore -eq 'LocalMachine') {
                Write-Warning 'On Linux systems you must use Currentuser as value of CertificateStore'
                $CertificateStore = 'CurrentUser'
            }
            
            $StoreName = [System.Security.Cryptography.X509Certificates.StoreName]::My 
            $StoreLocation = [System.Security.Cryptography.X509Certificates.StoreLocation]::$CertificateStore
            $Store = [System.Security.Cryptography.X509Certificates.X509Store]::new($StoreName, $StoreLocation) 
            $Store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
            $Certificate = $Store.Certificates | Where-Object {$_.Thumbprint -eq $CertificateThumbprint}

            if ($null -eq $Certificate) {
                $ParseInformation = $null
                $ParseCertificate = $null
            }
            else {
                $Scope = "https://graph.microsoft.com/.default"
                $Resource = "https://management.core.windows.net/"
        
                # Create base64 hash of certificate
                $CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash())
        
                # Create JWT timestamp for expiration
                $StartDate = (Get-Date "1970-01-01T00:00:00Z").ToUniversalTime()
                $JWTExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End (Get-Date).ToUniversalTime().AddMinutes(60)).TotalSeconds
                $JWTExpiration = [math]::Round($JWTExpirationTimeSpan,0)
        
                # Create JWT validity start timestamp
                $NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds
                $NotBefore = [math]::Round($NotBeforeExpirationTimeSpan,0)
        
                # Create JWT header
                $JWTHeader = @{
                    alg = "RS256"
                    typ = "JWT"
                    # Use the CertificateBase64Hash and replace/strip to match web encoding of base64
                    x5t = $CertificateBase64Hash -replace '\+','-' -replace '/','_' -replace '='
                }
        
                # Create JWT payload
                $JWTPayLoad = @{
                    # What endpoint is allowed to use this JWT
                    aud = "https://login.microsoftonline.com/$TenantID/oauth2/token"
                    # Expiration timestamp
                    exp = $JWTExpiration
                    # Issuer = your application
                    iss = $ClientID
                    # JWT ID: random guid
                    jti = [guid]::NewGuid()
                    # Not to be used before
                    nbf = $NotBefore
                    # JWT Subject
                    sub = $ClientID
                }
        
                # Convert header and payload to base64
                $JWTHeaderToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTHeader | ConvertTo-Json))
                $EncodedHeader = [System.Convert]::ToBase64String($JWTHeaderToByte)
        
                $JWTPayLoadToByte =  [System.Text.Encoding]::UTF8.GetBytes(($JWTPayload | ConvertTo-Json))
                $EncodedPayload = [System.Convert]::ToBase64String($JWTPayLoadToByte)
        
                # Join header and Payload with "." to create a valid (unsigned) JWT
                $JWT = $EncodedHeader + "." + $EncodedPayload
        
                # Get the private key object of your certificate
                $PrivateKey = $Certificate.PrivateKey
        
                # Define RSA signature and hashing algorithm
                $RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1
                $HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256
        
                # Create a signature of the JWT
                $Signature = [Convert]::ToBase64String(
                    $PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT),$HashAlgorithm,$RSAPadding)
                ) -replace '\+','-' -replace '/','_' -replace '='
        
                # Join the signature to the JWT with "."
                $JWT = $JWT + "." + $Signature
        
                # Create a hash with body parameters
                $Body = @{
                    client_id = $ClientID
                    client_assertion = $JWT
                    client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
                    scope = $Scope
                    grant_type = "client_credentials"
                    resource = $Resource
                }
        
                $Url = "https://login.microsoftonline.com/$TenantID/oauth2/token"
        
                # Use the self-generated JWT as Authorization
                $Header = @{
                    Authorization = "Bearer $JWT"
                }
        
                # Splat the parameters for Invoke-Restmethod for cleaner code
                $PostSplat = @{
                    ContentType = 'application/x-www-form-urlencoded'
                    Method = 'POST'
                    Body = $Body
                    Uri = $Url
                    Headers = $Header
                }
        
                $AccessToken = Invoke-RestMethod @PostSplat

                $ParseInformation = [pscustomobject]@{
                    'Token' = $AccessToken.access_token;
                    'ExpiresOn' = (Get-Date -UnixTimeSeconds $AccessToken.expires_on);
                    'Type' = $AccessToken.token_type;
                    'TenantId' = $TenantID;
                    'UserId' = $ClientID;
                }
            }

        }
        else{
            if ($DeviceAuthentication.IsPresent) {
                $Module = Get-Module -Name Az.Accounts -ListAvailable
                if($null -eq $Module){
                    Install-Module -Name Az.Accounts -Scope CurrentUser
                    Import-Module -Name Az.Accounts -Scope CurrentUser
                }
                else{
                    Import-Module -Name Az.Accounts -Scope CurrentUser
                }
                if($null -eq $TenantID){
                    Connect-AzAccount -UseDeviceAuthentication -ErrorAction SilentlyContinue -InformationAction SilentlyContinue | Out-Null
                }
                else{
                    Connect-AzAccount -Tenant $TenantID -UseDeviceAuthentication -ErrorAction SilentlyContinue -InformationAction SilentlyContinue | Out-Null
                }
                $AccessToken = Get-AzAccessToken

                $ParseInformation = [pscustomobject]@{
                    'Token' = $AccessToken.Token;
                    'ExpiresOn' = $AccessToken.ExpiresOn.LocalDateTime;
                    'Type' = $AccessToken.Type;
                    'TenantId' = $AccessToken.TenantId;
                    'UserId' = $AccessToken.UserId;
                }
            }
            else {
                $Module = Get-Module -Name Az.Accounts -ListAvailable
                if($null -eq $Module){
                    Install-Module -Name Az.Accounts -Scope CurrentUser
                    Import-Module -Name Az.Accounts -Scope Local
                }
                else{
                    Import-Module -Name Az.Accounts -Scope Local
                }

                if($null -eq $TenantID){
                    Connect-AzAccount -ErrorAction SilentlyContinue -InformationAction SilentlyContinue | Out-Null
                }
                else{
                    Connect-AzAccount -Tenant $TenantID -ErrorAction SilentlyContinue -InformationAction SilentlyContinue | Out-Null
                }
                $AccessToken = Get-AzAccessToken

                $ParseInformation = [pscustomobject]@{
                    'Token' = $AccessToken.Token;
                    'ExpiresOn' = $AccessToken.ExpiresOn.LocalDateTime;
                    'Type' = $AccessToken.Type;
                    'TenantId' = $AccessToken.TenantId;
                    'UserId' = $AccessToken.UserId;
                }
            }
        }
        
    }
    end{
        $ErrorActionPreference = "Continue"
        if ($null -eq $ParseInformation -and $null -eq $ParseCertificate) {
            Write-Error "Certificate not found on StoreLocation. Please install first."
        }
        elseif($null -eq $ParseInformation){
            Write-Error "Cannot get the bearer token with the supplied parameters. Please check the values."
        }
        else{
            New-Variable -Name "RevoAzBearerToken" -Value ($ParseInformation.Type + " " + $ParseInformation.Token) -Scope Global -Force -ErrorAction SilentlyContinue
            New-Variable -Name "RevoAzBearerTokenDetails" -Value $ParseInformation -Scope Global -Force -ErrorAction SilentlyContinue
            if(!$SecureOutput){
                Return $ParseInformation.Type + " " + $ParseInformation.Token
            }
        }
    }
}

function New-RevoAzMSALAccess{
    param(
        [parameter(Mandatory=$true, ParameterSetName = "ServicePrincipal")]
        [parameter(Mandatory=$false, ParameterSetName = "UserAuthentication")]
        [string]$TenantID,
        [parameter(Mandatory=$true, ParameterSetName = "ServicePrincipal")]
        [string]$ClientID,
        [parameter(Mandatory=$true, ParameterSetName = "ServicePrincipal")]
        [string]$CertificateLocation,
        [parameter(Mandatory=$true, ParameterSetName = "ServicePrincipal")]
        [securestring]$CertificatePassword,
        [parameter(Mandatory=$false, ParameterSetName = "ServicePrincipal")]
        [switch]$SecureOutput
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{

        $Module = Get-Module -Name MSAL.PS -ListAvailable
        if($null -eq $Module){
            Install-Module -Name MSAL.PS -Scope CurrentUser -Force -Confirm:$false;
            Import-Module -Name MSAL.PS -Scope CurrentUser
        }
        else{
            Import-Module -Name MSAL.PS -Scope CurrentUser
        }

        $CerLocValidation = Test-Path -Path $CertificateLocation -ErrorAction SilentlyContinue

        if(!$CerLocValidation){
            $CertLocationInvalid = $true
        }
        else {
            $ErrorActionPreference = 'SilentlyContinue'
            $Flag = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable 
            $Certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($CertificateLocation,$CertificatePassword,$Flag)
            if($Certificate){
                $ParseInformation = Get-MsalToken -ClientId $ClientID -TenantId $TenantID -ClientCertificate $Certificate -Scopes 'https://management.core.windows.net//.default'   
            }
            else {
                $CertPasswordInvalid = $true
            }

        }
        
    }
    end{
        $ErrorActionPreference = "Continue"
        if($CertLocationInvalid){
            Write-Error "Can't find the certificate. Please check your path."
        }
        elseif ($CertPasswordInvalid) {
            Write-Error "Certificate password incorrect. Please check the value."
        }
        else{
            New-Variable -Name "RevoAzBearerToken" -Value ($ParseInformation.TokenType + " " + $ParseInformation.AccessToken) -Scope Global -Force -ErrorAction SilentlyContinue
            New-Variable -Name "RevoAzBearerTokenDetails" -Value $ParseInformation -Scope Global -Force -ErrorAction SilentlyContinue
            if(!$SecureOutput){
                Return $ParseInformation.TokenType + " " + $ParseInformation.AccessToken
            }
        }
    }
}

function Get-RevoAzTenants{
    param(

    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{
        $AzureManagement = "https://management.azure.com"
        $ResourceURL = $AzureManagement + "/tenants?api-version=2020-01-01"

        $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
        if($null -ne $AccessToken){
            $Headers = @{}
            $Headers.Add("Authorization",$AccessToken)
    
            $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers -ErrorVariable InvokeError

            if ($InvokeError.Count -gt 0) {
                switch(($InvokeError.ErrorRecord.ErrorDetails.Message | ConvertFrom-Json -Depth 10).error.code){
                    'ExpiredAuthenticationToken' {
                        $BearerTokenExpired = $true
                    }
                    'AuthenticationFailedInvalidHeader'{
                        $BearerTokenInvalidHeader  = $true
                    }
                    'AuthenticationFailed'{
                        $BearerTokenFailed  = $true
                    }
                    default {
                        $BearerTokenError = $true
                    }
                }
            }
            else {
                $WebResponse = $WebRequest.Content | ConvertFrom-Json
                if($null -ne $WebResponse.nextLink){
                    do{
                        $AzTenants += $WebResponse.value
                        $WebRequest = Invoke-WebRequest -Method Get -Uri $WebResponse.nextLink -Headers $Headers
                        $WebResponse = $WebRequest.Content | ConvertFrom-Json
                    }
                    until($null -eq $WebResponse.nextLink)
                }
                else {
                    $AzTenants += $WebResponse.value
                }

                $Output = New-Object -TypeName System.Collections.ArrayList
                if($RevoAzBearerTokenDetails.UserId -like "*@*"){
                    foreach($Tenant in $AzTenants){
                        $ParseInformation = [pscustomobject]@{
                            'DisplayName' = $Tenant.displayName;
                            'DefaultDomain' = $Tenant.defaultDomain;
                            'TenantId' = $Tenant.tenantId;
                            'MicrosoftDomain' = ($Tenant.domains | Where-Object {$_ -like "*.onmicrosoft.com" -and $_ -notlike "*mail.onmicrosoft.com"});
                            'CustomDomains' = (($Tenant.domains | Where-Object {$_ -notlike "*.onmicrosoft.com"} ) -join ",");
                            'TenantCategory' = $Tenant.tenantCategory;
                            'Id' = $Tenant.id;
                        }
                        $Output.add($ParseInformation) | Out-Null
                        $ParseInformation = $null
                    }
                }
                else{
                    foreach($Tenant in $AzTenants){
                        $ParseInformation = [pscustomobject]@{
                            'TenantId' = $Tenant.tenantId;
                            'TenantCategory' = $Tenant.tenantCategory;
                            'Id' = $Tenant.id;
                        }
                        $Output.add($ParseInformation) | Out-Null
                        $ParseInformation = $null
                    }
                }
            }
            
        }
        else {
            $BearerTokenError = $true
        }
    }
    end{
        $ErrorActionPreference = "Continue"
        if($BearerTokenFailed){
            Write-Error "Your bearer token was invalid, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif ($BearerTokenInvalidHeader) {
            Write-Error "Your bearer token was malformed, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif ($BearerTokenExpired) {
            Write-Error "Your bearer token was expired, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif($BearerTokenError){
            Write-Error "Cannot get the bearer token, please first connect by New-RevoAzBearerByCertificate"
        }
        else{
            Return $Output
        }
    }
}

function Get-RevoAzSubscriptions{
    param(

    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{
        $AzureManagement = "https://management.azure.com"
        $ResourceURL = $AzureManagement + "/subscriptions?api-version=2020-01-01"

        $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
        if($null -ne $AccessToken){
            $Headers = @{}
            $Headers.Add("Authorization",$AccessToken)
    
            $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers -ErrorVariable InvokeError

            if ($InvokeError.Count -gt 0) {
                switch(($InvokeError.ErrorRecord.ErrorDetails.Message | ConvertFrom-Json -Depth 10).error.code){
                    'ExpiredAuthenticationToken' {
                        $BearerTokenExpired = $true
                    }
                    'AuthenticationFailedInvalidHeader'{
                        $BearerTokenInvalidHeader  = $true
                    }
                    'AuthenticationFailed'{
                        $BearerTokenFailed  = $true
                    }
                    default {
                        $BearerTokenError = $true
                    }
                }
            }
            else{
                $WebResponse = $WebRequest.Content | ConvertFrom-Json

                if($null -ne $WebResponse.nextLink){
                    do{
                        $AzSubscriptions += $WebResponse.value
                        $WebRequest = Invoke-WebRequest -Method Get -Uri $WebResponse.nextLink -Headers $Headers
                        $WebResponse = $WebRequest.Content | ConvertFrom-Json
                    }
                    until($null -eq $WebResponse.nextLink)
                }
                else {
                    $AzSubscriptions += $WebResponse.value
                }
    
                $Output = New-Object -TypeName System.Collections.ArrayList
                foreach($Subscription in $AzSubscriptions){
                    if($null -eq $Subscription.managedByTenants.TenantId){
                        $IsManaged = $false;
                    }
                    else{
                        $IsManaged = $true;
                    }
                    $ParseInformation = [pscustomobject]@{
                        'AuthorizationSource' = $Subscription.authorizationSource;
                        'SubscriptionId' = $Subscription.subscriptionId;
                        'TenantId' = $Subscription.tenantId;
                        'DisplayName' = $Subscription.displayName;
                        'State' = $Subscription.state;
                        'LocationPlacementId' = $Subscription.subscriptionPolicies.locationPlacementId;
                        'QuotaId' = $Subscription.subscriptionPolicies.quotaId;
                        'SpendingLimit' = $Subscription.subscriptionPolicies.spendingLimit;
                        'Id' = $Subscription.id;
                        'IsManaged' = $IsManaged;
                    }
                    $Output.add($ParseInformation) | Out-Null
                    $ParseInformation = $null
                }
            }
        }
        else {
            $BearerTokenError = $true
        }
    }
    end{
        $ErrorActionPreference = "Continue"
        if($BearerTokenFailed){
            Write-Error "Your bearer token was invalid, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif ($BearerTokenInvalidHeader) {
            Write-Error "Your bearer token was malformed, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif ($BearerTokenExpired) {
            Write-Error "Your bearer token was expired, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif($BearerTokenError){
            Write-Error "Cannot get the bearer token, please first connect by New-RevoAzBearerByCertificate"
        }
        else{
            Return $Output
        }
    }
}

function Get-RevoAzLocations{
    param(
        [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)]
        [ValidateNotNull()]
        [string]$SubscriptionId
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{
        $AzureManagement = "https://management.azure.com"
        $ResourceURL = $AzureManagement + "/subscriptions/$SubscriptionId/locations?api-version=2020-01-01"

        $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
        if($null -ne $AccessToken){
            $Headers = @{}
            $Headers.Add("Authorization",$AccessToken)
    
            $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers -ErrorVariable InvokeError

            if ($InvokeError.Count -gt 0) {
                switch(($InvokeError.ErrorRecord.ErrorDetails.Message | ConvertFrom-Json -Depth 10).error.code){
                    'ExpiredAuthenticationToken' {
                        $BearerTokenExpired = $true
                    }
                    'AuthenticationFailedInvalidHeader'{
                        $BearerTokenInvalidHeader  = $true
                    }
                    'AuthenticationFailed'{
                        $BearerTokenFailed  = $true
                    }
                    default {
                        $BearerTokenError = $true
                    }
                }
            }
            else{
                $WebResponse = $WebRequest.Content | ConvertFrom-Json
                if($null -ne $WebResponse.nextLink){
                    do{
                        $AzLocations += $WebResponse.value
                        $WebRequest = Invoke-WebRequest -Method Get -Uri $WebResponse.nextLink -Headers $Headers
                        $WebResponse = $WebRequest.Content | ConvertFrom-Json
                    }
                    until($null -eq $WebResponse.nextLink)
                }
                else {
                    $AzLocations += $WebResponse.value
                }

                $Output = New-Object -TypeName System.Collections.ArrayList
                foreach($Location in $AzLocations){
                    $ParseInformation = [pscustomobject]@{
                        'Name' = $Location.name;
                        'DisplayName' = $Location.displayName;
                        'RegionalDisplayName' = $Location.regionalDisplayName;
                        'RegionType' = $Location.metadata.regionType;
                        'RegionCategory' = $Location.metadata.regionCategory;
                        'GeographyGroup' = $Location.metadata.geographyGroup;
                        'Longitude' = $Location.metadata.longitude;
                        'Latitude' = $Location.metadata.latitude;
                        'PhysicalLocation' = $Location.metadata.physicalLocation;
                        'PairedRegionName' = ($Location.metadata.pairedRegion.name -Join ",");
                        'PairedRegionId' = ($Location.metadata.pairedRegion.Id -Join ",");
                    } 
                    $Output.add($ParseInformation) | Out-Null
                    $ParseInformation = $null
                }
            }
        }
        else {
            $BearerTokenError = $true
        }
    }
    end{
        $ErrorActionPreference = "Continue"
        if($BearerTokenFailed){
            Write-Error "Your bearer token was invalid, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif ($BearerTokenInvalidHeader) {
            Write-Error "Your bearer token was malformed, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif ($BearerTokenExpired) {
            Write-Error "Your bearer token was expired, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif($BearerTokenError){
            Write-Error "Cannot get the bearer token, please first connect by New-RevoAzBearerByCertificate"
        }
        else{
            Return $Output
        }
    }
}

function Get-RevoAzResourceGroups{
    param(
        [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)]
        [ValidateNotNull()]
        [string]$SubscriptionId
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{
        $AzureManagement = "https://management.azure.com"
        $ResourceURL = $AzureManagement + "/subscriptions/$SubscriptionId/resourcegroups?api-version=2020-10-01"

        $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
        if($null -ne $AccessToken){
            $Headers = @{}
            $Headers.Add("Authorization",$AccessToken)
    
            $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers -ErrorVariable InvokeError
            if ($InvokeError.Count -gt 0) {
                switch(($InvokeError.ErrorRecord.ErrorDetails.Message | ConvertFrom-Json -Depth 10).error.code){
                    'ExpiredAuthenticationToken' {
                        $BearerTokenExpired = $true
                    }
                    'AuthenticationFailedInvalidHeader'{
                        $BearerTokenInvalidHeader  = $true
                    }
                    'AuthenticationFailed'{
                        $BearerTokenFailed  = $true
                    }
                    default {
                        $BearerTokenError = $true
                    }
                }
            }
            else{
                $WebResponse = $WebRequest.Content | ConvertFrom-Json
                if($null -ne $WebResponse.nextLink){
                    do{
                        $AzResourceGroups += $WebResponse.value
                        $WebRequest = Invoke-WebRequest -Method Get -Uri $WebResponse.nextLink -Headers $Headers
                        $WebResponse = $WebRequest.Content | ConvertFrom-Json
                    }
                    until($null -eq $WebResponse.nextLink)
                }
                else {
                    $AzResourceGroups += $WebResponse.value
                }

                $Output = New-Object -TypeName System.Collections.ArrayList
                foreach($ResourceGroup in $AzResourceGroups){
                    
                    if($null -eq $ResourceGroup.tags){
                        $IsTagged = 'Untagged'
                    }
                    else{
                        $IsTagged = 'Tagged'
                    }

                    $ParseInformation = [pscustomobject]@{
                        'Name' = $ResourceGroup.name;
                        'Location' = $ResourceGroup.location;
                        'IsTagged' = $IsTagged;
                        'Tags' = ($ResourceGroup.tags | Measure-Object).Count;
                        'Type' = $ResourceGroup.type;
                        'Id' = $ResourceGroup.id;
                        'SubscriptionId' = (($ResourceGroup.id -split "/")[2]);
                    }
                    $Output.add($ParseInformation) | Out-Null
                    $ParseInformation = $null
                }
            }
        }
        else {
            $BearerTokenError = $true
        }
    }
    end{
        $ErrorActionPreference = "Continue"
        if($BearerTokenFailed){
            Write-Error "Your bearer token was invalid, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif ($BearerTokenInvalidHeader) {
            Write-Error "Your bearer token was malformed, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif ($BearerTokenExpired) {
            Write-Error "Your bearer token was expired, please reconnect with New-RevoAzBearerByCertificate"
        }
        elseif($BearerTokenError){
            Write-Error "Cannot get the bearer token, please first connect by New-RevoAzBearerByCertificate"
        }
        else{
            Return $Output
        }
    }
}

function Get-RevoAzResources{
    param(
        [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)]
        [ValidateNotNull()]
        [string]$SubscriptionId
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{
        $AzureManagement = "https://management.azure.com"
        $ResourceURL = $AzureManagement + "/subscriptions/$SubscriptionId/resources?" + '$expand' + "=createdTime,provisioningState,changedTime&api-version=2020-10-01"

        $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
        if($null -ne $AccessToken){
            $Headers = @{}
            $Headers.Add("Authorization",$AccessToken)

            $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers
            $WebResponse = $WebRequest.Content | ConvertFrom-Json
            do{
                $AzResources += $WebResponse.value
                $WebRequest = Invoke-WebRequest -Method Get -Uri $WebResponse.nextLink -Headers $Headers
                $WebResponse = $WebRequest.Content | ConvertFrom-Json
            }
            until($null -eq $WebResponse.nextLink)

            $Output = New-Object -TypeName System.Collections.ArrayList
            foreach($Resource in $AzResources){

                if($null -eq $Resource.tags){
                    $IsTagged = $false
                }
                else{
                    $IsTagged = $true
                }

                $ParseInformation = [pscustomobject]@{
                    'Name' = $Resource.name;
                    'Type' = $Resource.type;
                    'ResourceGroup' = (($Resource.id -split "/")[4]);
                    'Location' = $Resource.location;
                    'SubscriptionId' = (($Resource.id -split "/")[2]);
                    'SKUName' = $Resource.sku.name
                    'PlanName' = $Resource.plan.name
                    'Kind' = $Resource.kind;
                    'ManagedBy' = $Resource.managedBy;
                    'Identity' = ([string]$Resource.identity -replace "@{" -replace "}");
                    'PlanProduct' = $Resource.plan.product
                    'PlanPromotionCode' = $Resource.plan.promotionCode
                    'PlanPublisher' = $Resource.plan.publisher
                    'PlanVersion' = $Resource.plan.version
                    'ProvisioningState' = $Resource.provisioningState;
                    'SKUCapacity' = $Resource.sku.capacity
                    'SKUFamily' = $Resource.sku.family
                    'SKUModel' = $Resource.sku.model
                    'SKUSize' = $Resource.sku.size
                    'SKUTier' = $Resource.sku.tier
                    'Zones' = ($Resource.Zones -join ",");
                    'Tags' = ([string]$Resource.tags -replace "@{" -replace "}");
                    'IsTagged' = $IsTagged;
                    'SystemData' = ([string]$Resource.SystemData -replace "@{" -replace "}");
                    'Id' = $Resource.id;
                }
                $Output.add($ParseInformation) | Out-Null
                $ParseInformation = $null
            }
        }
        else {
            $BearerTokenError = $true
        }
    }
    end{
        $ErrorActionPreference = "Continue"
        if($BearerTokenError){
            Write-Error "Cannot get the bearer token, please first connect by New-RevoAzBearerByCertificate"
        }
        else{
            Return $Output
        }
    }
}

function Get-RevoAzVm{
    param(
        [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)]
        [ValidateNotNull()]
        [string]$SubscriptionId
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
        $TextInfo = (Get-Culture).TextInfo
    }
    process{
        $AzureManagement = "https://management.azure.com"
        $ResourceURL = $AzureManagement + "/subscriptions/$SubscriptionId/providers/Microsoft.Compute/virtualMachines?api-version=2020-12-01"

        $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
        if($null -ne $AccessToken){
            $Headers = @{}
            $Headers.Add("Authorization",$AccessToken)

            $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers
            $WebResponse = $WebRequest.Content | ConvertFrom-Json
            do{
                $AzVms += $WebResponse.value
                $WebRequest = Invoke-WebRequest -Method Get -Uri $WebResponse.nextLink -Headers $Headers
                $WebResponse = $WebRequest.Content | ConvertFrom-Json
            }
            until($null -eq $WebResponse.nextLink)

            $Output = New-Object -TypeName System.Collections.ArrayList
            foreach($Vm in $AzVms){
                
                $ResourceURL = $AzureManagement + "$($VM.id)/instanceView?api-version=2020-12-01"

                $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
                $Headers = @{}
                $Headers.Add("Authorization",$AccessToken)
    
                $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers
                $WebResponse = $WebRequest.Content | ConvertFrom-Json

                if($null -eq $Vm.tags){
                    $IsTagged = $false
                }
                else{
                    $IsTagged = $true
                }
                if($null -ne $Vm.properties.osProfile.windowsConfiguration){
                    $OperatingSystem = "Windows"
                    $ProvisionVMAgent = $Vm.properties.osProfile.windowsConfiguration.provisionVMAgent;
                    $PatchMode = $VM.properties.osProfile.windowsConfiguration.patchSettings.patchMode;
                    $EnableAutomaticUpdates = $VM.properties.osProfile.windowsConfiguration.enableAutomaticUpdates;
                    $DisablePasswordAuthentication = $false;
                    $PublicKeysLocation =$null;
                }
                else{
                    $OperatingSystem = "Linux"
                    $ProvisionVMAgent = $Vm.properties.osProfile.linuxconfiguration.provisionVMAgent;
                    $PatchMode = $VM.properties.osProfile.linuxconfiguration.patchSettings.patchMode;
                    $EnableAutomaticUpdates = $null;
                    $DisablePasswordAuthentication = $VM.properties.osProfile.linuxconfiguration.disablePasswordAuthentication;
                    $PublicKeysLocation =$VM.properties.osProfile.linuxconfiguration.ssh.publicKeys.path;
                }
                if($null -ne $Vm.properties.storageProfile.osDisk.vhd.uri){
                    $DiskOSNeedMigrateDisk = $true;
                }
                else{
                    $DiskOSNeedMigrateDisk = $false;
                }
                if($null -ne $VM.properties.diagnosticsProfile.bootDiagnostics.storageUri){
                    $BootDiagnosticsNeedMigrate = $true;
                }
                else{
                    $BootDiagnosticsNeedMigrate = $false;
                }

                $ParseInformation = [pscustomobject]@{
                    'Name' = $Vm.name;
                    'ResourceGroup' = ($VM.id -split "/")[4];
                    'SubscriptionId' = (($Resource.id -split "/")[2]);
                    'Location' = $Vm.location;
                    'LastStatus' = $TextInfo.ToTitleCase($WebResponse.statuses[1].code -replace 'PowerState/');
                    'LastStatusTime' = $WebResponse.statuses[0].time;
                    'ComputerName' = $Vm.properties.osProfile.computerName;
                    'VmSize' = $Vm.properties.hardwareProfile.vmSize;
                    'OperatingSystem' = $OperatingSystem;
                    'Zones' = ($Vm.Zones -join ",");
                    'AdminUsername' = $Vm.properties.osProfile.adminUsername;
                    'VmId' = $Vm.properties.vmId;
                    'ImagePublisher' = $Vm.properties.storageProfile.imageReference.publisher;
                    'ImageOffer' = $Vm.properties.storageProfile.imageReference.offer;
                    'ImageSKU' = $Vm.properties.storageProfile.imageReference.sku;
                    'ImageVersion' = $Vm.properties.storageProfile.imageReference.version;
                    'ImageExactVersion' = $Vm.properties.storageProfile.imageReference.exactVersion;
                    'DiskOSName' = $Vm.properties.storageProfile.osDisk.name;
                    'DiskOSSizeGB' = $Vm.properties.storageProfile.osDisk.diskSizeGB;
                    'DiskOSCreateOption' = $Vm.properties.storageProfile.osDisk.createOption;
                    'DiskOSCaching' = $Vm.properties.storageProfile.osDisk.caching;
                    'DiskOSWriteAcceleratorEnabled' = $Vm.properties.storageProfile.osDisk.writeAcceleratorEnabled;
                    'DiskOSId' = $Vm.properties.storageProfile.osDisk.managedDisk.id;
                    'DiskOSNeedMigrateDisk' = $DiskOSNeedMigrateDisk;
                    'DiskDataCount' = $VM.properties.storageProfile.dataDisks.Count;
                    'DiskDataNames' = ($VM.properties.storageProfile.dataDisks.name -join ",");
                    'DiskDataIds' = ($VM.properties.storageProfile.dataDisks.managedDisk.id -join ",");
                    'ProvisionVMAgent' = $ProvisionVMAgent;
                    'PatchMode' = $PatchMode;
                    'EnableAutomaticUpdates' = $EnableAutomaticUpdates;
                    'DisablePasswordAuthentication' = $DisablePasswordAuthentication;
                    'PublicKeysLocation' = $PublicKeysLocation;
                    'NetworkInterfaces' = $VM.properties.networkProfile.networkInterfaces.Count;
                    'NetworkInterfacesId' = $VM.properties.networkProfile.networkInterfaces.Id -join ",";
                    'BootDiagnostics' = $VM.properties.diagnosticsProfile.bootDiagnostics.enabled;
                    'BootDiagnosticsStorage' = $VM.properties.diagnosticsProfile.bootDiagnostics.storageUri;
                    'BootDiagnosticsNeedMigrate' = $BootDiagnosticsNeedMigrate;
                    'ProvisioningState' = $Vm.properties.provisioningState;
                    'Extensions' = ($VM.resources -replace ("@{id=" + $VM.id + "/extensions/") -replace "}" -join ",");
                    'ExtensionsNumber' = $Vm.resources.Count;
                    'Tags' = ([string]$Vm.tags -replace "@{" -replace "}");
                    'IsTagged' = $IsTagged;
                    'Type' = $Vm.type;
                    'Id' = $Vm.id;
                }
                $Output.add($ParseInformation) | Out-Null
                $ParseInformation = $null
            }
        }
        else {
            $BearerTokenError = $true
        }
    }
    end{
        $ErrorActionPreference = "Continue"
        if($BearerTokenError){
            Write-Error "Cannot get the bearer token, please first connect by New-RevoAzBearerByCertificate"
        }
        else{
            Return $Output
        }
    }
}

function Get-RevoAzAdvisor{
    param(
        [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)]
        [ValidateNotNull()]
        [string]$SubscriptionId,
        [parameter(Mandatory=$false, ParameterSetName = "MoreDetails")]
        [switch]$MoreDetails,
        [parameter(Mandatory=$false, ParameterSetName = "MoreDetails")]
        [ValidateSet("Daily","Weekly","Monthly", IgnoreCase = $true)]
        [string]$AggregationLevel
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{
        $AzureManagement = "https://management.azure.com"
        $ResourceURL = $AzureManagement + "/subscriptions/$SubscriptionId/providers/Microsoft.Advisor/AdvisorScore?api-version=2020-07-01-preview"

        $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
        if($null -ne $AccessToken){
            $Headers = @{}
            $Headers.Add("Authorization",$AccessToken)

            $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers
            $WebResponse = $WebRequest.Content | ConvertFrom-Json
            do{
                $AzAdvisor += $WebResponse.value
                $WebRequest = Invoke-WebRequest -Method Get -Uri $WebResponse.nextLink -Headers $Headers
                $WebResponse = $WebRequest.Content | ConvertFrom-Json
            }
            until($null -eq $WebResponse.nextLink)

            $Output = New-Object -TypeName System.Collections.ArrayList
            if($MoreDetails.IsPresent){
                foreach($Advisor in $AzAdvisor){

                    foreach($TimeSeries in $Advisor.properties.timeSeries){
                        $AggregationLevelCount = 0
                        do{
                            foreach ($ScoreHistory in $TimeSeries[$AggregationLevelCount].scoreHistory) {
                                if($AggregationLevel -eq "Daily"){
                                    if($TimeSeries[$AggregationLevelCount].aggregationLevel -eq "Daily"){
                                        $ParseInformation = [pscustomobject]@{
                                            'Name' = $Advisor.name;
                                            'Date' = $ScoreHistory.date;
                                            'Score' = $ScoreHistory.score;
                                            'ConsumptionUnits' = $ScoreHistory.consumptionUnits;
                                            'ImpactedResourceCount' = $ScoreHistory.impactedResourceCount;
                                            'PotentialScoreIncrease' = $ScoreHistory.potentialScoreIncrease;
                                            'AggregationLevel' = $TimeSeries[$AggregationLevelCount].aggregationLevel;
                                            'SubscriptionId' = (($Resource.id -split "/")[2]);
                                        }
                                        $Output.add($ParseInformation) | Out-Null
                                        $ParseInformation = $null
                                    }
                                }
                                elseif($AggregationLevel -eq "Weekly"){
                                    if($TimeSeries[$AggregationLevelCount].aggregationLevel -eq "Weekly"){
                                        $ParseInformation = [pscustomobject]@{
                                            'Name' = $Advisor.name;
                                            'Date' = $ScoreHistory.date;
                                            'Score' = $ScoreHistory.score;
                                            'ConsumptionUnits' = $ScoreHistory.consumptionUnits;
                                            'ImpactedResourceCount' = $ScoreHistory.impactedResourceCount;
                                            'PotentialScoreIncrease' = $ScoreHistory.potentialScoreIncrease;
                                            'AggregationLevel' = $TimeSeries[$AggregationLevelCount].aggregationLevel;
                                            'SubscriptionId' = (($Resource.id -split "/")[2]);
                                        }
                                        $Output.add($ParseInformation) | Out-Null
                                        $ParseInformation = $null
                                    }
                                }
                                elseif($AggregationLevel -eq "Monthly"){
                                    if($TimeSeries[$AggregationLevelCount].aggregationLevel -eq "Monthly"){
                                        $ParseInformation = [pscustomobject]@{
                                            'Name' = $Advisor.name;
                                            'Date' = $ScoreHistory.date;
                                            'Score' = $ScoreHistory.score;
                                            'ConsumptionUnits' = $ScoreHistory.consumptionUnits;
                                            'ImpactedResourceCount' = $ScoreHistory.impactedResourceCount;
                                            'PotentialScoreIncrease' = $ScoreHistory.potentialScoreIncrease;
                                            'AggregationLevel' = $TimeSeries[$AggregationLevelCount].aggregationLevel;
                                            'SubscriptionId' = (($Resource.id -split "/")[2]);
                                        }
                                        $Output.add($ParseInformation) | Out-Null
                                        $ParseInformation = $null
                                    }
                                }
                                else{
                                    $ParseInformation = [pscustomobject]@{
                                        'Name' = $Advisor.name;
                                        'Date' = $ScoreHistory.date;
                                        'Score' = $ScoreHistory.score;
                                        'ConsumptionUnits' = $ScoreHistory.consumptionUnits;
                                        'ImpactedResourceCount' = $ScoreHistory.impactedResourceCount;
                                        'PotentialScoreIncrease' = $ScoreHistory.potentialScoreIncrease;
                                        'AggregationLevel' = $TimeSeries[$AggregationLevelCount].aggregationLevel;
                                        'SubscriptionId' = (($Resource.id -split "/")[2]);
                                    }
                                    $Output.add($ParseInformation) | Out-Null
                                    $ParseInformation = $null
                                }
                            }
                            $AggregationLevelCount++
                        }
                        until($AggregationLevelCount -eq $TimeSeries.AggregationLevel.Length)
                    }
                }
            }
            else{
                foreach($Advisor in $AzAdvisor){
                    if($Advisor.name -notlike "*-*"){
                        $ParseInformation = [pscustomobject]@{
                            'Name' = $Advisor.name;
                            'Date' = $Advisor.properties.lastRefreshedScore.date;
                            'Score' = $Advisor.properties.lastRefreshedScore.score;
                            'ConsumptionUnits' = $Advisor.properties.lastRefreshedScore.consumptionUnits;
                            'ImpactedResourceCount' = $Advisor.properties.lastRefreshedScore.impactedResourceCount;
                            'PotentialScoreIncrease' = $Advisor.properties.lastRefreshedScore.potentialScoreIncrease;
                            'Type' = $Advisor.type;
                            'Id' = $Advisor.id;
                        }
                    }
                    $Output.add($ParseInformation) | Out-Null
                    $ParseInformation = $null
                }
            }

        }
        else {
            $BearerTokenError = $true
        }
    }
    end{
        $ErrorActionPreference = "Continue"
        if($BearerTokenError){
            Write-Error "Cannot get the bearer token, please first connect by New-RevoAzBearerByCertificate"
        }
        else{
            Return $Output
        }
    }
}

function Get-RevoAzSecureScore{
    param(
        [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)]
        [ValidateNotNull()]
        [string]$SubscriptionId
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{
        $AzureManagement = "https://management.azure.com"
        $ResourceURL = $AzureManagement + "/subscriptions/$SubscriptionId/providers/Microsoft.Security/secureScores?api-version=2020-01-01"

        $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
        if($null -ne $AccessToken){
            $Headers = @{}
            $Headers.Add("Authorization",$AccessToken)

            $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers
            $WebResponse = $WebRequest.Content | ConvertFrom-Json
            do{
                $AzSecureScore += $WebResponse.value
                $WebRequest = Invoke-WebRequest -Method Get -Uri $WebResponse.nextLink -Headers $Headers
                $WebResponse = $WebRequest.Content | ConvertFrom-Json
            }
            until($null -eq $WebResponse.nextLink)
            if($SubscriptionId.GetType().BaseType.Name -eq "Array"){
                return "sdsdsd"
            }

            $Output = New-Object -TypeName System.Collections.ArrayList
            foreach($SecureScore in $AzSecureScore){
                $ParseInformation = [pscustomobject]@{
                    'DisplayName' = $SecureScore.properties.displayName;
                    'Name' = $SecureScore.name;
                    'SubscriptionId' = (($Resource.id -split "/")[2]);
                    'ScoreMax' = $SecureScore.properties.score.max;
                    'ScoreCurrent' = $SecureScore.properties.score.current;
                    'ScorePercentageNumber' = $SecureScore.properties.score.percentage;
                    'ScorePercentage' = $SecureScore.properties.score.percentage.ToString("P");
                    'Weight' = $SecureScore.properties.weight;
                }
                $Output.add($ParseInformation) | Out-Null
                $ParseInformation = $null
            }
        }
        else {
            $BearerTokenError = $true
        }
    }
    end{
        $ErrorActionPreference = "Continue"
        if($BearerTokenError){
            Write-Error "Cannot get the bearer token, please first connect by New-RevoAzBearerByCertificate"
        }
        else{
            Return $Output
        }
    }
}

function Get-RevoAzSecureScoreControls{
    param(
        [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)]
        [ValidateNotNull()]
        [string]$SubscriptionId,
        [parameter(Mandatory=$false)]
        [switch]$MoreDetails
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{
        $AzureManagement = "https://management.azure.com"
        $ResourceURL = $AzureManagement + "/subscriptions/$SubscriptionId/providers/Microsoft.Security/secureScores/ascScore/secureScoreControls?api-version=2020-01-01&" + '$expand' + "=definition"

        $AccessToken = Get-Variable -Name "RevoAzBearerToken" -ValueOnly -ErrorAction SilentlyContinue
        if($null -ne $AccessToken){
            $Headers = @{}
            $Headers.Add("Authorization",$AccessToken)

            $WebRequest = Invoke-WebRequest -Method Get -Uri $ResourceURL -Headers $Headers
            $WebResponse = $WebRequest.Content | ConvertFrom-Json
            do{
                $AzSecureScoreControls += $WebResponse.value
                $WebRequest = Invoke-WebRequest -Method Get -Uri $WebResponse.nextLink -Headers $Headers
                $WebResponse = $WebRequest.Content | ConvertFrom-Json
            }
            until($null -eq $WebResponse.nextLink)

            $Output = New-Object -TypeName System.Collections.ArrayList
            if($MoreDetails.IsPresent){
                foreach($SecureScoreControls in $AzSecureScoreControls){
                    foreach ($AssessmentDefinitions in $secureScoreControls.properties.definition.properties.assessmentDefinitions) {
                        $ParseInformation = [pscustomobject]@{
                            'DisplayName' = $SecureScoreControls.properties.displayName;
                            'HealthyResourceCount' = $SecureScoreControls.properties.healthyResourceCount;
                            'UnhealthyResourceCount' = $SecureScoreControls.properties.unhealthyResourceCount;
                            'NotApplicableResourceCount' = $SecureScoreControls.properties.notApplicableResourceCount;
                            'ScoreCurrent' = $SecureScoreControls.properties.score.current;
                            'ScoreMax' = $SecureScoreControls.properties.score.max;
                            'ScorePercentage' = $SecureScoreControls.properties.score.percentage;
                            'Weight' = $SecureScoreControls.properties.weight;
                            'DefinitionId' = $SecureScoreControls.properties.definition.id;
                            'DefinitionName' = $SecureScoreControls.properties.definition.name;
                            'DefinitionType' = $SecureScoreControls.properties.definition.type;
                            'DefinitionSourceType' = $SecureScoreControls.properties.definition.properties.source.sourceType;
                            'DefinitionDisplayName' = $SecureScoreControls.properties.definition.properties.displayName;
                            'DefinitionMaxScore' = $SecureScoreControls.properties.definition.properties.maxScore;
                            'AssessmentDefinitionsId' = $AssessmentDefinitions.id;
                            'AssessmentDefinitionsName' = $AssessmentDefinitions.Id.Substring($AssessmentDefinitions.Id.LastIndexOf("/")+1)
                            'Name' = $SecureScoreControls.name;
                            'Type' = $SecureScoreControls.type;
                            'Id' = $SecureScoreControls.id;
                            'SubscriptionId' = (($Resource.id -split "/")[2]);
                        }
                        $Output.add($ParseInformation) | Out-Null
                        $ParseInformation = $null
                    }
                }
            }
            else{
                foreach($SecureScoreControls in $AzSecureScoreControls){
                    $ParseInformation = [pscustomobject]@{
                        'DisplayName' = $SecureScoreControls.properties.displayName;
                        'HealthyResourceCount' = $SecureScoreControls.properties.healthyResourceCount;
                        'UnhealthyResourceCount' = $SecureScoreControls.properties.unhealthyResourceCount;
                        'NotApplicableResourceCount' = $SecureScoreControls.properties.notApplicableResourceCount;
                        'ScoreCurrent' = $SecureScoreControls.properties.score.current;
                        'ScoreMax' = $SecureScoreControls.properties.score.max;
                        'ScorePercentage' = $SecureScoreControls.properties.score.percentage;
                        'Weight' = $SecureScoreControls.properties.weight;
                        'Name' = $SecureScoreControls.name;
                        'Type' = $SecureScoreControls.type;
                        'Id' = $SecureScoreControls.id;
                        'SubscriptionId' = (($Resource.id -split "/")[2]);
                    }
                    $Output.add($ParseInformation) | Out-Null
                    $ParseInformation = $null
                }
            }
        }
        else {
            $BearerTokenError = $true
        }
    }
    end{
        $ErrorActionPreference = "Continue"
        if($BearerTokenError){
            Write-Error "Cannot get the bearer token, please first connect by New-RevoAzBearerByCertificate"
        }
        else{
            Return $Output
        }
    }
}

function New-RevoAzTable{
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNull()]
        [string]$StorageName,
        [parameter(Mandatory=$true)]
        [ValidateNotNull()]
        [string]$TableName,
        [parameter(Mandatory=$true)]
        [ValidateNotNull()]
        [string]$Signature
    )
    begin{
        $ErrorActionPreference = "SilentlyContinue"
    }
    process{
        
        $UrlBase = "https://$StorageName.table.core.windows.net/Tables"
        $url = $UrlBase + $Signature
        
        $Headers = @{
            Accept = "application/json;odata=nometadata"
            "Content-Type" = "application/json"
        }
        
        $Body = New-Object PSObject
        $Body | Add-Member -type NoteProperty -Name 'TableName' -Value $TableName.Trim()
        $Body = $Body | ConvertTo-Json
        
        $WebRequest = Invoke-WebRequest -Method Post -Uri $url -Headers $Headers -Body $Body
        $LastError = $Error[0]
    }
    end{
        $ErrorActionPreference = "Continue"
        if($null -ne $WebRequest){
            Return $WebRequest.StatusDescription
        }
        else{
            Write-Error ($LastError.ErrorDetails.Message | ConvertFrom-Json).'odata.error'.code
        }
    }
}