Module.psm1

#REQUIRES -Version 5 -Modules @{ModuleName='Avanade.Azure.Models';ModuleVersion='1.0.3'},@{ModuleName='Avanade.ArmTools';ModuleVersion="1.6.2"}
using module Avanade.Azure.Models

#region concrete classes

Class ResourceBase:ArmResourceBase
{

    [System.Management.Automation.ActionPreference] $VerbosePreference='SilentlyContinue'

    static [string] $ARMFrontDoorUri='https://management.azure.com'

    static [int] $ActivityId=80001

    [bool]SupportsMetrics()
    {
        if([String]::IsNullOrEmpty($this.Id))
        {
            throw "the resource id is null"
        }
        $Detail=$this.Id|ConvertFrom-ArmResourceId
        return $Detail.FullResourceType -in @(
            "Microsoft.Compute/virtualMachines","Microsoft.ClassicCompute/virtualMachines",
            "Microsoft.Sql/servers/databases","Microsoft.Web/sites",
            "Microsoft.Web/serverFarms",
            "Microsoft.StreamAnalytics/streamingjobs","Microsoft.Devices/IotHubs",
            "Microsoft.ServiceBus/namespaces","Microsoft.Compute/virtualMachineScaleSets" #,"microsoft.insights/components"
        )
    }

    [bool]IsClassic()
    {
        if([String]::IsNullOrEmpty($this.Id))
        {
            throw "the resource id is null"
        }
        $Detail=$this.Id|ConvertFrom-ArmResourceId
        return $Detail.Namespace -like "*.Classic*"
    }

    [ArmMetricDefinition[]]MetricDefinitions([string]$AccessToken)
    {
        if($this.SupportsMetrics())
        {
            return Get-ArmResourceMetricDefinition -ResourceId $this.Id -AccessToken $AccessToken -ApiEndpoint ([ResourceBase]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
        }
        else
        {
            Write-Warning "$($this.Type) does not support metrics"
            return $null
        }
    }

    [ArmNormalizedMetricValue[]]Metrics([string]$AccessToken,[DateTime]$Start,[DateTime]$End,[string]$AggregationType,[string]$Granularity)
    {
        [ArmNormalizedMetricValue[]]$Metrics=@()
        $MetricDefinitions=$this.MetricDefinitions($AccessToken)
        for ($i = 0; $i -lt $MetricDefinitions.Count; $i++)
        {
            Write-Progress -Id ([ResourceBase]::ActivityId) -Activity "Gathering Normalized Metrics" `
                -PercentComplete (($i/$MetricDefinitions.Count) * 100) `
                -Status "Retrieving $($MetricDefinitions[$i].Name.LocalizedValue) metrics for $($MetricDefinitions[$i].ResourceId)"
            Write-Verbose "Retrieving $($MetricDefinitions[$i].Name.LocalizedValue) metrics for $($MetricDefinitions[$i].ResourceId)"
            $MetricValues=$this.MetricValues($AccessToken,$MetricDefinitions[$i],$Start,$End,$AggregationType,$Granularity)
            $Metrics+=$MetricValues
        }
        Write-Progress -Id ([ResourceBase]::ActivityId) -Activity "Gathering Normalized Metrics" -Completed
        return $Metrics
    }

    [ArmNormalizedMetricValue[]]MetricValues([string]$AccessToken,[ArmMetricDefinition]$MetricDefinition,[DateTime]$Start,[DateTime]$End,[string]$AggregationType,[string]$Granularity)
    {
        [ArmNormalizedMetricValue[]]$Metrics=@()
        $IsClassic=$this.IsClassic()
        $StartTime=(New-Object System.DateTimeOffset($Start)).ToString('o')
        $EndTime=(New-Object System.DateTimeOffset($End)).ToString('o')
        if($IsClassic)
        {
            $Filter="(name.value eq '$($MetricDefinition.Name.Value)') and startTime eq $($StartTime) " +
                "and endTime eq $($EndTime) and timeGrain eq duration'$Granularity'"
            [ArmClassicMetricEntry]$MetricResult=Get-ArmResourceMetric -ResourceId $MetricDefinition.ResourceId -Filter $Filter -AccessToken $AccessToken -ApiEndpoint ([ResourceBase]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
            foreach ($ItemData in $MetricResult.MetricValues) {
                if($ItemData.PSobject.Properties.name -match $AggregationType)
                {
                    $MetricValue=$ItemData|Select-Object -ExpandProperty $AggregationType
                }
                else
                {
                    $MetricValue=0.0
                }
                $NormalEntry=[ArmNormalizedMetricValue]::new()
                $NormalEntry.AggregationType=$AggregationType
                $NormalEntry.Name=$MetricDefinition.Name
                $NormalEntry.PrimaryAggregationType=$MetricDefinition.PrimaryAggregationType
                $NormalEntry.ResourceId=$MetricDefinition.ResourceId
                $NormalEntry.Unit=$MetricDefinition.Unit
                $NormalEntry.TimeStamp=$ItemData.TimeStamp
                $NormalEntry.MetricValue=$MetricValue
                $NormalEntry.ResourceType=$this.Type
                $Metrics+=$NormalEntry
            }
        }
        else
        {
            $Filter="(name.value eq '$($MetricDefinition.Name.Value)') and aggregationType eq '$AggregationType' and startTime eq $($StartTime) " +
                "and endTime eq $($EndTime) and timeGrain eq duration'$Granularity'"
            switch ($AggregationType)
            {
                'Average' {
                    [ArmAverageMetricEntry]$MetricResult=Get-ArmResourceMetric -ResourceId $MetricDefinition.ResourceId -Filter $Filter -AccessToken $AccessToken -ApiEndpoint ([ResourceBase]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
                    break
                }
                'Maximum' {
                    [ArmMaximumMetricEntry]$MetricResult=Get-ArmResourceMetric -ResourceId $MetricDefinition.ResourceId -Filter $Filter -AccessToken $AccessToken -ApiEndpoint ([ResourceBase]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
                    break
                }
                'Minimum' {
                    [ArmMinimumMetricEntry]$MetricResult=Get-ArmResourceMetric -ResourceId $MetricDefinition.ResourceId -Filter $Filter -AccessToken $AccessToken -ApiEndpoint ([ResourceBase]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
                    break
                }
                'Total' {
                        $MetricResult=Get-ArmResourceMetric -ResourceId $MetricDefinition.ResourceId -Filter $Filter -AccessToken $AccessToken -ApiEndpoint ([ResourceBase]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
                        break
                    }
                Default {
                    [ArmMetricEntry]$MetricResult=Get-ArmResourceMetric -ResourceId $MetricDefinition.ResourceId -Filter $Filter -AccessToken $AccessToken -ApiEndpoint ([ResourceBase]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
                    break
                }
            }
            if($MetricResult -ne $null)
            {
                foreach ($ItemData in $MetricResult.Data) {
                    if($ItemData.PSobject.Properties.name -match $AggregationType)
                    {
                        $MetricValue=$ItemData|Select-Object -ExpandProperty $AggregationType
                    }
                    else
                    {
                        $MetricValue=0.0
                    }
                    $NormalEntry=[ArmNormalizedMetricValue]::new()
                    $NormalEntry.AggregationType=$AggregationType
                    $NormalEntry.Name=$MetricDefinition.Name
                    $NormalEntry.PrimaryAggregationType=$MetricDefinition.PrimaryAggregationType
                    $NormalEntry.ResourceId=$MetricDefinition.ResourceId
                    $NormalEntry.Unit=$MetricDefinition.Unit
                    $NormalEntry.TimeStamp=$ItemData.TimeStamp
                    $NormalEntry.MetricValue=$MetricValue
                    $NormalEntry.ResourceType=$this.Type
                    $Metrics+=$NormalEntry
                }
            }
        }
        return $Metrics
    }
}

Class ResourceInstance:ResourceBase
{
    [System.Object]$Properties
    [ArmResourceBase[]]$Resources
}

class SubscriptionInstance:ArmSubscriptionBase
{
    static [int] $ActivityId=8001

    static [string] $ARMFrontDoorUri='https://management.azure.com'

    [ResourceBase[]]Resources([string]$AccessToken)
    {
        $this.TestId()
        return $this.Resources($AccessToken,$true)
    }

    [ResourceBase[]]Resources([string]$AccessToken,[bool]$InstanceView)
    {
        $this.TestId()
        $Activity="Retrieving Resources for $($this.DisplayName) Subscription"
        [ResourceBase[]]$BaseResources=Get-ArmResource -SubscriptionId $this.SubscriptionId -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
        if ($InstanceView)
        {
            $RequestLength=0
            [ResourceInstance[]]$Instances=@()
            for ($i = 0; $i -lt $BaseResources.Count; $i++)
            {
                $BaseResource=$BaseResources[$i]
                $BaseResource.VerbosePreference=$this.VerbosePreference
                $CurrentProgress=(($i/$BaseResources.Count) * 100)
                $CurrentStatus="Retrieving $($BaseResource.id)"
                Write-Verbose "[$($this.SubscriptionId)] $CurrentStatus - %($CurrentProgress)"
                Write-Progress -Id ([SubscriptionInstance]::ActivityId) -Activity $Activity -Status $CurrentStatus -PercentComplete $CurrentProgress
                $Instances+=(Get-ArmResourceInstance -AccessToken $AccessToken -Id $BaseResource.Id -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference)
            }
            Write-Progress -Id ([SubscriptionInstance]::ActivityId) -Activity $Activity -Completed
            return $Instances
        }
        else
        {
            return $BaseResources
        }
    }

    [ArmUsageAggregate[]]UsageAggregates([string]$AccessToken,[DateTime]$Start,[DateTime]$End,[bool]$ShowDetails,[string]$Granularity)
    {
        $this.TestId()
        [ArmUsageAggregate[]]$Usages=Get-ArmUsageAggregate -AccessToken $AccessToken `
            -SubscriptionId $this.SubscriptionId `
            -StartTime $Start -EndTime $End `
            -Granularity $Granularity -ShowDetails:$ShowDetails `
            -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
        return $Usages
    }

    [ArmRateCard]RateCard([string]$AccessToken,[string]$OfferCode,[string]$Region,[string]$Locale)
    {
        $this.TestId()
        return Get-ArmRateCard -SubscriptionId $this.SubscriptionId -AccessToken $AccessToken `
            -OfferCode $OfferCode -RegionInfo $Region -Locale $Locale `
            -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [ArmEventLogEntry[]]EventLog([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmEventLog -SubscriptionId $this.SubscriptionId -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [ArmNormalizedMetricValue[]]ResourceMetrics([string]$AccessToken,[DateTime]$Start,[DateTime]$End,[string]$AggregationType,[string]$Granularity)
    {
        $this.TestId()
        [ArmNormalizedMetricValue[]]$ResourceMetrics=@()
        [ResourceBase[]]$SubscriptionResources=$this.Resources($AccessToken,$false)
        $Activity="Retrieving Resource Metrics for $($this.DisplayName) Subscription"
        for ($i = 0; $i -lt $SubscriptionResources.Count; $i++)
        {
            $CurrentProgress=(($i/$SubscriptionResources.Count) * 100)
            $CurrentStatus="Retrieving Metrics for $($SubscriptionResources[$i].Id)"
            Write-Verbose "[$($this.SubscriptionId)] $CurrentStatus - %($CurrentProgress)"
            Write-Progress -Id ([SubscriptionInstance]::ActivityId) -Activity $Activity -Status $CurrentStatus -PercentComplete $CurrentProgress
            $Metrics=$SubscriptionResources[$i].Metrics($AccessToken,$Start,$End,$AggregationType,$Granularity)
            $ResourceMetrics+=$Metrics
        }
        Write-Progress -Id ([SubscriptionInstance]::ActivityId) -Activity $Activity -Completed
        return $ResourceMetrics
    }

    [ArmAdvisorRecommendation[]]AdvisorRecommendations([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmAdvisorRecommendation -Subscription $this -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [ArmPreviewFeature[]]PreviewFeatures([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmFeature -Subscription $this -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [ArmResourceProvider[]]ResourceProviders([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmProvider -Subscription $this -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri)
    }

    [ArmRoleDefinition[]]RoleDefinitions([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmRoleDefinition -Subscription $this -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [ArmRoleAssignment[]]RoleAssignments([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmRoleAssignment -Subscription $this -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [ArmPolicyDefinition[]]PolicyDefinitions([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmPolicyDefinition -Subscription $this -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [ArmPolicyAssignment[]]PolicyAssignments([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmPolicyAssignment -Subscription $this -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [ArmResourceLock[]]ResourceLocks([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmResourceLock -Subscription $this -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [ArmQuotaUsage[]]StorageQuotaUsage([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmStorageUsage -Subscription $this -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri)
    }

    [ComputeQuotaUsage[]]ComputeQuotaUsage([string]$AccessToken,[string]$Location)
    {
        $this.TestId()
        [ArmQuotaUsage[]]$UsageResult=Get-ArmComputeUsage -Subscription $this -AccessToken $AccessToken -Location $Location -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
        $Result=$UsageResult|Select-Object @{N="Usage";E={[ComputeQuotaUsage]::new($_,$Location)}}
        return $Result|Select-Object -ExpandProperty Usage
    }

    [ComputeQuotaUsage[]]ComputeQuotaUsage([string]$AccessToken)
    {
        $this.TestId()
        [ComputeQuotaUsage[]]$Usages=@()
        $Locations=Get-ArmResourceTypeLocation -Subscription $this -ResourceType 'Microsoft.Compute/virtualMachines' -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
        foreach ($Location in $Locations)
        {
            $Usages+=$this.ComputeQuotaUsage($AccessToken,$Location)
        }
        return $Usages
    }

    [ArmTagName[]]TagNameReport([string]$AccessToken)
    {
        $this.TestId()
        return Get-ArmTagName -Subscription $this -AccessToken $AccessToken -ExpandTagValues -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
    }

    [VmSize[]]AvailableVmSizes([string]$AccessToken)
    {
        [VmSize[]]$VmSizes=@()
        $Locations=Get-ArmResourceTypeLocation -Subscription $this -ResourceType 'Microsoft.Compute/virtualMachines' -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
        foreach ($Location in $Locations)
        {
            $VmSizes+=$this.AvailableVmSizes($AccessToken,$Location)
        }
        return $VmSizes
    }

    [VmSize[]]AvailableVmSizes([string]$AccessToken,[string]$Location)
    {
        $this.TestId()
        [ArmVmSize[]]$VmSizes=Get-ArmVmSize -Subscription $this -Location $Location -AccessToken $AccessToken -ApiEndpoint ([SubscriptionInstance]::ARMFrontDoorUri) -Verbose:$this.VerbosePreference
        $Result=$VmSizes|Select-Object @{N="Size";E={[VmSize]::new($_,$Location)}}
        return $Result|Select-Object -ExpandProperty "Size"
    }

}

Class TenantInstance:GraphTenantBase
{

    [GraphDomain[]]Domains([string]$AccessToken)
    {
        return Get-AzureADGraphDomain -AccessToken $AccessToken
    }

    [GraphSigninEvent[]]SigninEvents([string]$AccessToken,[datetime]$Start,[datetime]$End)
    {
        $StartTime="{0:s}" -f $Start + "Z"
        $EndTime="{0:s}" -f $End + "Z"
        return Get-AzureADGraphSigninEvent -TenantName $this.TenantId -AccessToken $AccessToken -Filter "signinDateTime gt $StartTime and signinDateTime lt $EndTime" -Verbose:$this.VerbosePreference
    }

    [GraphAuditEvent[]]AuditEvents([string]$AccessToken,[datetime]$Start,[datetime]$End)
    {
        $StartTime="{0:s}" -f $Start + "Z"
        $EndTime="{0:s}" -f $End + "Z"
        return Get-AzureADGraphAuditEvent -TenantName $this.TenantId -AccessToken $AccessToken -Filter "activityDate gt $StartTime and activityDate lt $EndTime" -Verbose:$this.VerbosePreference
    }

    [GraphDirectoryGroup[]]Groups([string]$AccessToken)
    {
        return Get-AzureADGraphGroup -AccessToken $AccessToken -Verbose:$this.VerbosePreference
    }

    [GraphDirectoryUser[]]Users([string]$AccessToken)
    {
        return Get-AzureADGraphUser -AccessToken $AccessToken -Verbose:$this.VerbosePreference
    }

    [GraphDirectoryRole[]]Roles([string]$AccessToken)
    {
        return Get-AzureADGraphRole -AccessToken $AccessToken -Verbose:$this.VerbosePreference
    }

    [GraphDirectoryRoleTemplate[]]RoleTemplates([string]$AccessToken)
    {
        return Get-AzureADGraphRoleTemplate -AccessToken $AccessToken
    }

    [GraphOauth2PermissionGrant[]]OauthPermissionGrants([string]$AccessToken)
    {
        return Get-AzureADGraphOauthPermissionGrant -AccessToken $AccessToken -Top 999 -Verbose:$this.VerbosePreference
    }

    [GraphDirectoryServicePrincipal[]]ServicePrincipals([string]$AccessToken)
    {
        return Get-AzureADGraphServicePrincipal -AccessToken $AccessToken -Verbose:$this.VerbosePreference
    }

    [GraphDirectoryApplication[]]Applications([string]$AccessToken)
    {
        return Get-AzureADGraphApplication -AccessToken $AccessToken -Verbose:$this.VerbosePreference
    }

    TenantInstance([string] $TenantId)
    {
        $this.TenantId=$TenantId
    }
}

Class SummaryReport
{
    [string]$ReportType

    [Object]Export()
    {
        throw "this must be implemented in a derived class"
    }
}

Class SummaryExport
{
    [string]$ReportType
}

Class SubscriptionSummaryExport:SummaryExport
{
    [String]$SubscriptionId
    [String]$SubscriptionDisplayName
    [Object[]]$RateCards
    [Object[]]$EventLogEntries
    [Object[]]$AdvisorRecommendations
    [Object[]]$Resources
    [Object[]]$ResourceLocks
    [Object[]]$PolicyDefinitions
    [Object[]]$RoleAssignments
    [Object[]]$RoleDefinitions
    [Object[]]$PolicyAssignments
    [Object[]]$StorageQuotaUsage
    [Object[]]$ComputeQuotaUsage
    [Object[]]$TagNameUsage
    [Object[]]$MetricValues
    [Object[]]$UsageAggregates
    [Object[]]$AvailableVmSizes
}

Class TenantSummaryExport:SummaryExport
{
    [String]$TenantId
    [Object[]]$Groups
    [Object[]]$Roles
    [Object[]]$RoleTemplates
    [Object[]]$Users
    [Object[]]$AuditEvents
    [Object[]]$SigninEvents
    [Object[]]$OauthPermissionGrants
    [Object[]]$Applications
    [Object[]]$ServicePrincipals
}

Class SubscriptionSummary:SummaryReport
{
    static [int] $ActivityId=9001

    [SubscriptionInstance]$Subscription
    [ResourceBase[]]$Resources
    [ArmRateCard]$RateCard
    [ArmUsageAggregate[]]$UsageAggregates
    [ArmAdvisorRecommendation[]]$AdvisorRecommendations
    [ArmEventLogEntry[]]$EventLogEntries
    [ArmNormalizedMetricValue[]]$MetricValues
    [ArmPreviewFeature[]]$PreviewFeatures
    [ArmResourceProvider[]]$ResourceProviders
    [ArmRoleDefinition[]]$RoleDefinitions
    [ArmRoleAssignment[]]$RoleAssignments
    [ArmPolicyDefinition[]]$PolicyDefinitions
    [ArmPolicyAssignment[]]$PolicyAssignments
    [ArmResourceLock[]]$ResourceLocks
    [ArmQuotaUsage[]]$StorageQuotaUsage
    [ComputeQuotaUsage[]]$ComputeQuotaUsage
    [ArmTagName[]]$TagNameReport
    [VmSize[]]$AvailableVmSizes

    [SubscriptionSummaryExport]Export()
    {
        $Result=[SubscriptionSummaryExport]::new()
        $Result.SubscriptionId=$this.Subscription.SubscriptionId
        $Result.SubscriptionDisplayName=$this.Subscription.DisplayName
        $Result.ReportType=$this.ReportType
        $Result.ResourceLocks=$this|Export-SubscriptionResourceLocks
        $Result.MetricValues=$this|Export-SubscriptionMetricSet
        $Result.UsageAggregates=$this|Export-SubscriptionUsageAggregates
        $Result.RateCards=$this|Export-SubscriptionRateCard
        #$Result.PreviewFeatures=$this.PreviewFeatures
        $Result.StorageQuotaUsage=$this|Export-SubscriptionStorageQuotaUsage
        $Result.ComputeQuotaUsage=$this|Export-SubscriptionComputeQuotaUsage
        $Result.AvailableVmSizes=$this|Export-SubscriptionVmSizes
        $Result.TagNameUsage=$this|Export-SubscriptionTagNameReport
        $Result.AdvisorRecommendations=$this|Export-SubscriptionRecommendations
        $Result.EventLogEntries=$this|Export-SubscriptionEventlog
        $Result.PolicyAssignments=$this|Export-SubscriptionPolicyAssignments
        $Result.PolicyDefinitions=$this|Export-SubscriptionPolicyDefinitions
        #$Result.ResourceProviders=$this.ResourceProviders
        $Result.RoleAssignments=$this|Export-SubscriptionRoleAssignments
        $Result.RoleDefinitions=$this|Export-SubscriptionRoleDefinitions
        $Result.Resources=$this|Export-SubscriptionResources
        return $Result
    }

    SubscriptionSummary([SubscriptionInstance]$subscription)
    {
        $this.ReportType='Resource'
        $this.Subscription=$subscription
    }
}

Class TenantSummary:SummaryReport
{
    static [int] $ActivityId=9002

    [TenantSummaryExport]Export()
    {
        $Result=[TenantSummaryExport]::new()
        $Result.TenantId=$this.Tenant.TenantId
        $Result.ReportType=$this.ReportType
        $Result.AuditEvents=$this.AuditEvents
        $Result.SigninEvents=$this.SigninEvents
        $Result.Groups=$this.Groups
        $Result.Roles=$this.Roles
        $Result.RoleTemplates=$this.RoleTemplates
        $Result.Users=$this.Users
        $Result.OauthPermissionGrants=$this.OauthPermissionGrants
        $Result.Applications=$this.Applications
        $Result.ServicePrincipals=$this.ServicePrincipals
        return $Result
    }

    [TenantInstance]$Tenant
    [GraphDomain[]]$Domains
    [GraphSigninEvent[]]$SigninEvents
    [GraphAuditEvent[]]$AuditEvents
    [GraphDirectoryGroup[]]$Groups
    [GraphDirectoryUser[]]$Users
    [GraphDirectoryRole[]]$Roles
    [GraphDirectoryRoleTemplate[]]$RoleTemplates
    [GraphOauth2PermissionGrant[]]$OauthPermissionGrants
    [GraphDirectoryApplication[]]$Applications
    [GraphDirectoryServicePrincipal[]]$ServicePrincipals

    TenantSummary([TenantInstance]$Tenant)
    {
        $this.ReportType='Tenant'
        $this.Tenant=$Tenant
    }
}

Class DetailReport
{
    [SummaryReport[]]$Summaries
}

Class VmSize:ArmVmSize
{
    [string]$Location

    VmSize([ArmVmSize]$VmSize,[string]$Location)
    {
        $this.Location=$Location
        $this.MaxDataDiskCount=$VmSize.MaxDataDiskCount
        $this.MemoryInMB=$VmSize.MemoryInMB
        $this.Name=$VmSize.Name
        $this.OsDiskSizeInMB=$VmSize.OsDiskSizeInMB
        $this.ResourceDiskSizeInMB=$VmSize.ResourceDiskSizeInMB
        $this.NumberOfCores=$VmSize.NumberOfCores
    }
}

Class ComputeQuotaUsage:ArmQuotaUsage
{
    [string]$Location

    ComputeQuotaUsage([ArmQuotaUsage]$ArmQuotaUsage,[string]$Location)
    {
        $this.Unit=$ArmQuotaUsage.Unit
        $this.CurrentValue=$ArmQuotaUsage.CurrentValue
        $this.Limit=$ArmQuotaUsage.Limit
        $this.Name=$ArmQuotaUsage.Name
        $this.Location=$Location
    }
}

#endregion

#region Functions

Function GetTenantSummary
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantInstance[]]$Tenant,
        [Parameter(Mandatory=$true)]
        [String]
        $AccessToken,
        [Parameter(Mandatory=$true)]
        [datetime]
        $Start,
        [Parameter(Mandatory=$true)]
        [datetime]
        $End,
        [Parameter(Mandatory=$false)]
        [Switch]
        $Events,
        [Parameter(Mandatory=$false)]
        [Switch]
        $OauthPermissionGrants,
        [Parameter(Mandatory=$false)]
        [Switch]
        $ServicePrincipals,
        [Parameter(Mandatory=$false)]
        [Switch]
        $Applications
    )

    BEGIN
    {
        $ActivityId=[TenantSummary]::ActivityId
    }
    PROCESS
    {
        foreach ($item in $Tenant)
        {
            $item.VerbosePreference=$VerbosePreference
            $TenantSummary=[TenantSummary]::new($item)
            Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving Domains" -PercentComplete 10
            $TenantSummary.Domains=$item.Domains($AccessToken)

            Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving Groups" -PercentComplete 20
            $TenantSummary.Groups=$item.Groups($AccessToken)

            Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving Users" -PercentComplete 30
            $TenantSummary.Users=$item.Users($AccessToken)

            if ($OauthPermissionGrants.IsPresent)
            {
                Write-Progress -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving OAuth2 Permission Grants" -PercentComplete 40
                $TenantSummary.OauthPermissionGrants=$item.OauthPermissionGrants($AccessToken)
            }

            Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving Role Templates" -PercentComplete 50
            $TenantSummary.RoleTemplates=$item.RoleTemplates($AccessToken)

            Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving Roles" -PercentComplete 55
            $TenantSummary.Roles=$item.Roles($AccessToken)

            if($Applications.IsPresent)
            {
                Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving Applications" -PercentComplete 65
                $TenantSummary.Applications=$item.Applications($AccessToken)
            }

            if($ServicePrincipals.IsPresent)
            {
                Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving ServicePrincipals" -PercentComplete 60
                $TenantSummary.ServicePrincipals=$item.ServicePrincipals($AccessToken)
            }

            if($Events.IsPresent)
            {
                Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving Audit Events $($Start) - $($End)" -PercentComplete 70
                $TenantSummary.AuditEvents=$item.AuditEvents($AccessToken,$Start,$End)
                Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Status "Retrieving Signin Events $($Start) - $($End)" -PercentComplete 80
                $TenantSummary.SigninEvents=$item.SigninEvents($AccessToken,$Start,$End)
            }

            Write-Progress -Id $ActivityId -Activity "Summarizing Azure AD Tenant $($item.TenantId)" -Completed
            Write-Output $TenantSummary
        }
    }
    END
    {

    }
}

Function GetSubscriptionSummary
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionInstance[]]
        $Subscription,
        [Parameter(Mandatory=$false)]
        [System.Uri]
        $ArmFrontDoorUri='https://management.azure.com',
        [Parameter(Mandatory=$true)]
        [String]
        $AccessToken,
        [Parameter(Mandatory=$true)]
        [String]
        $OfferId,
        [Parameter(Mandatory=$true)]
        [datetime]
        $End,
        [Parameter(Mandatory=$true)]
        [datetime]
        $Start,
        [Parameter(Mandatory=$false)]
        [String]
        $Region="US",
        [Parameter(Mandatory=$false)]
        [String]
        $Locale="en-US",
        [ValidateSet('Average','Maximum','Minimum','Total')]
        [Parameter(Mandatory=$false)]
        [String]
        $AggregationType="Average",
        [Parameter(Mandatory=$false)]
        [String]
        $MetricGranularity="PT1H",
        [ValidateSet('Hourly','Daily')]
        [Parameter(Mandatory=$false)]
        [String]
        $UsageGranularity="Hourly",
        [Parameter(Mandatory=$false)]
        [Switch]
        $Usage,
        [Parameter(Mandatory=$false)]
        [Switch]
        $Metrics,
        [Parameter(Mandatory=$false)]
        [Switch]
        $InstanceData
    )
    BEGIN
    {
        $ActivityId=8008
    }
    PROCESS
    {
        foreach ($item in $Subscription)
        {
            $Result=[SubscriptionSummary]::new($item)

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Resource Providers" -PercentComplete 5
            $Result.ResourceProviders=$item.ResourceProviders($AccessToken)

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Preview Features" -PercentComplete 10
            $Result.PreviewFeatures=$item.PreviewFeatures($AccessToken)

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Rate Card" -PercentComplete 15
            $Result.RateCard=$item.RateCard($AccessToken,$OfferId,$Region,$Locale);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Advisor Recommendations" -PercentComplete 20
            $Result.AdvisorRecommendations=$item.AdvisorRecommendations($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Event Log" -PercentComplete 25
            $Result.EventLogEntries=$item.EventLog($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Role Definitions" -PercentComplete 30
            $Result.RoleDefinitions=$item.RoleDefinitions($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Role Assignments" -PercentComplete 35
            $Result.RoleAssignments=$item.RoleAssignments($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Policy Definitions" -PercentComplete 40
            $Result.PolicyDefinitions=$item.PolicyDefinitions($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Policy Assignments" -PercentComplete 45
            $Result.PolicyAssignments=$item.PolicyAssignments($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Resource Locks" -PercentComplete 45
            $Result.ResourceLocks=$item.ResourceLocks($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Resource Tag Report" -PercentComplete 50
            $Result.TagNameReport=$item.TagNameReport($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Resources" -PercentComplete 55
            $Result.Resources=[ResourceBase[]]$item.Resources($AccessToken,$InstanceData.IsPresent);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Storage Quota Usage" -PercentComplete 60
            $Result.StorageQuotaUsage=[ArmQuotaUsage[]]$item.StorageQuotaUsage($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Compute Quota Usage" -PercentComplete 65
            $Result.ComputeQuotaUsage=[ComputeQuotaUsage[]]$item.ComputeQuotaUsage($AccessToken);

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Available VM Sizes" -PercentComplete 65
            $Result.AvailableVmSizes=[VmSize[]]$item.AvailableVmSizes($AccessToken);

            if($Metrics.IsPresent)
            {
                Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Metrics" -PercentComplete 75
                $Result.MetricValues=$item.ResourceMetrics($AccessToken,$Start.ToLocalTime(),$End.ToLocalTime(),$AggregationType,$MetricGranularity)
            }
            if($Usage.IsPresent)
            {
                Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Status "Retrieving Usage Aggregates" -PercentComplete 85
                $Result.UsageAggregates=$item.UsageAggregates($AccessToken,$Start,$End,$true,$UsageGranularity)
            }

            Write-Progress -Id $ActivityId -Activity "Summarizing Subscription $($item.DisplayName)" -Completed
            Write-Output $Result
        }
    }
}

Function Get-SubscriptionSummary
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $AccessToken,
        [Parameter(Mandatory=$false)]
        [System.Uri]
        $ArmFrontDoorUri='https://management.azure.com',
        [Parameter(Mandatory=$true)]
        [String]
        $OfferId,
        [Parameter(Mandatory=$true)]
        [datetime]
        $End,
        [Parameter(Mandatory=$true)]
        [datetime]
        $Start,
        [Parameter(Mandatory=$false)]
        [String]
        $Region="US",
        [Parameter(Mandatory=$false)]
        [String]
        $Locale="en-US",
        [ValidateSet('Average','Maximum','Minimum','Total')]
        [Parameter(Mandatory=$false)]
        [String]
        $AggregationType="Average",
        [Parameter(Mandatory=$false)]
        [String]
        $MetricGranularity="PT1H",
        [ValidateSet('Hourly','Daily')]
        [Parameter(Mandatory=$false)]
        [String]
        $UsageGranularity="Hourly",
        [Parameter(Mandatory=$false)]
        [Switch]
        $Usage,
        [Parameter(Mandatory=$false)]
        [Switch]
        $Metrics,
        [Parameter(Mandatory=$false)]
        [Switch]
        $InstanceData,
        [Parameter(Mandatory=$false)]
        [ScriptBlock]
        $SubscriptionFilter={$_.DisplayName -ne 'Access To Azure Active Directory'}
    )
    Write-Verbose "Using ARM Front Door:$ArmFrontDoorUri"
    [SubscriptionInstance]::ARMFrontDoorUri=$ArmFrontDoorUri
    [ResourceBase]::ARMFrontDoorUri=$ArmFrontDoorUri
    [SubscriptionInstance[]]$Subscriptions=Get-ArmSubscription -AccessToken $AccessToken -ApiEndpoint $ArmFrontDoorUri|Where-Object -FilterScript $SubscriptionFilter
    for ($i = 0; $i -lt $Subscriptions.Count; $i++)
    {
        $Subscription=$Subscriptions[$i]
        $Subscription.VerbosePreference=$VerbosePreference
        Write-Progress -Activity "Gathering Subscription Summaries" -Status "Gathering summary of $($Subscription.DisplayName) subscription" -PercentComplete ((($i+1)/$Subscriptions.Count) * 100)
        Write-Output $Subscription|GetSubscriptionSummary -AccessToken $AccessToken -OfferId $OfferId `
            -Start $Start -End $End -Region $Region -Locale $Locale `
            -AggregationType $AggregationType -MetricGranularity $MetricGranularity -UsageGranularity $UsageGranularity `
            -InstanceData:$InstanceData.IsPresent `
            -Usage:$Usage.IsPresent -Metrics:$Metrics.IsPresent -ArmFrontDoorUri $ArmFrontDoorUri
    }
    Write-Progress -Activity "Gathering Subscription Summaries" -Completed
}

Function Get-TenantSummary
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true)]
        [String[]]
        $TenantId,
        [Parameter(Mandatory=$true)]
        [String]
        $AccessToken,
        [Parameter(Mandatory=$true)]
        [datetime]
        $Start,
        [Parameter(Mandatory=$true)]
        [datetime]
        $End,
        [Parameter(Mandatory=$false)]
        [Switch]
        $Events,
        [Parameter(Mandatory=$false)]
        [Switch]
        $OauthPermissionGrants,
        [Parameter(Mandatory=$false)]
        [Switch]
        $ServicePrincipals,
        [Parameter(Mandatory=$false)]
        [Switch]
        $Applications
    )
    [TenantInstance[]]$Tenants=$TenantId|Select-Object -Property @{N='Value';E={[TenantInstance]::new($_)}}|Select-Object -ExpandProperty Value
    Write-Output $Tenants|GetTenantSummary -AccessToken $AccessToken `
    -Events:$TenantEvents.IsPresent -OAuthPermissionGrants:$OAuthPermissionGrants.IsPresent `
    -Applications:$Applications.IsPresent -ServicePrincipals:$ServicePrincipals.IsPresent `
    -Start $Start -End $End
}

#region Graph Export

Function Export-TenantUsers
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantSummary[]]$Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('AccountEnabled','AlternativeSignInNamesInfo','City','CompanyName','Country','CreationType',
            'DeletionTimestamp','Department','Description','DirSyncEnabled','DisplayName','FacsimileTelephoneNumber',
            @{N='AssignedLicensesSku';E={$_.AssignedLicenses|Select-Object -ExpandProperty SkuId}},
            @{N='AssignedPlansAssignedTimestamp';E={$_.AssignedPlans|Select-Object -ExpandProperty AssignedTimestamp}},
            @{N='AssignedPlansServicePlanId';E={$_.AssignedPlans|Select-Object -ExpandProperty ServicePlanId}},
            @{N='AssignedPlansCapabilityStatus';E={$_.AssignedPlans|Select-Object -ExpandProperty CapabilityStatus}},
            @{N='AssignedPlansService';E={$_.AssignedPlans|Select-Object -ExpandProperty Service}},
            @{N='ThumbnailEditLink';E={$_|Select-Object -ExpandProperty 'ThumbnailPhoto@odata.mediaEditLink'}}
            'PasswordPolicies',
            @{N='ForceChangePasswordNextLogin';E={$_.PasswordProfile|Select-Object -ExpandProperty ForceChangePasswordNextLogin}}
            'GivenName','ImmutableId','IsCompromised','JobTitle','LastDirSyncTime','Mail','MailNickname','Mobile','ObjectId','ObjectType',
            'OnPremisesSecurityIdentifier','OtherMails','PasswordProfile','PhysicalDeliveryOfficeName','PostalCode',
            'PreferredLanguage','ProvisioningErrors','ProxyAddresses','SipProxyAddress','State','StreetAddress',
            'Surname','TelephoneNumber','UsageLocation','UserPrincipalName','UserType')
            Write-Output $item.Users|Select-Object -Property $SelectScope
        }
    }
    END
    {

    }
}

Function Export-TenantGroups
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantSummary[]]$Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('DeletionTimestamp','Description','DirSyncEnabled','DisplayName','LastDirSyncTime',
                                'Mail','MailEnabled','MailNickname','ObjectId','ObjectType',
                                'OnPremisesSecurityIdentifier','ProvisioningErrors','ProxyAddresses','SecurityEnabled'
                            )
            Write-Output $item.Groups|Select-Object -Property $SelectScope
        }
    }
    END
    {

    }
}

Function Export-TenantRoles
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantSummary[]]$Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('DeletionTimestamp','Description','DisplayName','IsSystem','ObjectId','ObjectType','RoleDisabled','RoleTemplateId')
            Write-Output $item.Roles|Select-Object -Property $SelectScope
        }
    }
    END
    {

    }
}

Function Export-TenantAuditEvents
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantSummary[]]$Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('Activity','ActivityDate','ActivityDateInMillis','ActivityOperationType',
                'ActivityResultDescription','ActivityResultStatus','ActivityType','Actor','ActorType',
                'AdditionalDetails','Category','ComponentOrSource','CorrelationId','DomainName',
                'Id','InternalCorrelationId','Source','TenantGeolocation','TenantId','TenantName',
                @{N='ActorName';E={$_.Actor.Name}},
                @{N='ActorIpAddress';E={$_.Actor.IPAddress}},
                @{N='ActorObjectId';E={$_.Actor.ObjectId}},
                @{N='ActorServicePrincipalName';E={$_.Actor.ServicePrincipalName}}
            )
            Write-Output $item.AuditEvents|Select-Object -Property $SelectScope
        }
    }
    END
    {

    }
}

Function Export-TenantSigninEvents
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantSummary[]]$Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('AppDisplayName','AppId','DeviceInformation',
                @{N='Latitude';E={$_.GeoCoordinates.Latitude}},
                @{N='Longitude';E={$_.GeoCoordinates.Longitude}},
                @{N='City';E={$_.GeoCoordinates.City}},
                @{N='Country';E={$_.GeoCoordinates.Country}},
                @{N='State';E={$_.GeoCoordinates.State}},
                'Id','IpAddress','LoginStatus',
                'SigninDateTime','SigninDateTimeInMillis','UserDisplayName','UserId','UserPrincipalName')
            Write-Output $item.SigninEvents|Select-Object -Property $SelectScope
        }
    }
    END
    {

    }
}

Function Export-TenantRoleTemplates
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantSummary[]]$Summary
    )
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('DisplayName','Description','ObjectId','ObjectType','DeletionTimestamp')
            Write-Output $item.RoleTemplates|Select-Object -Property $SelectScope
        }
    }
}

Function Export-TenantOauthPermissionGrants
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantSummary[]]$Summary
    )
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('ClientId','ConsentType','ExpiryTime','ObjectId','PrincipalId','ResourceId','Scope','StartTime')
            Write-Output $item.OauthPermissionGrants|Select-Object -Property $SelectScope
        }
    }
}

Function Export-TenantApplications
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantSummary[]]$Summary
    )
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('AppId','ObjectType','DisplayName','ObjectId','Oauth2Permissions','HomePage','Oauth2AllowImplicitFlow',
                'Oauth2AllowUrlPathMatching','Oauth2RequirePostResponse','PublicClient','GroupMembershipClaims','AvailableToOtherTenants'
            )
            Write-Output $item.Applications|Select-Object -Property $SelectScope
        }
    }
}

Function Export-TenantServicePrincipals
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [TenantSummary[]]$Summary
    )
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('AppId','ObjectType','DisplayName','ObjectId','AccountEnabled'
                ,'Oauth2Permissions','HomePage','AlternativeNames',
                'AppDisplayName','AppOwnerTenantId','PublisherName','ServicePrincipalNames','ServicePrincipalType',
                'AppRoleAssignmentRequired','Tags'
            )
            Write-Output $item.ServicePrincipals|Select-Object -Property $SelectScope
        }
    }
}

#endregion

#region ARM Export

Function Export-SubscriptionResources
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('Id','Kind','Name','Type','Location',
                @{N='SkuName';E={$_.Sku.Name}},
                @{N='SkuTier';E={$_.Sku.Tier}}
            )
            Write-Output $item.Resources|Select-Object -Property $SelectScope
        }
    }
    END
    {
    }
}

Function Export-SubscriptionStorageQuotaUsage
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if ($item.StorageQuotaUsage -ne $null) {
                $SelectScope=@()
                $SelectScope=@('Unit','CurrentValue','Limit',
                    @{N='Name';E={$_.Name.Value}}
                    @{N='LocalizedName';E={$_.Name.LocalizedValue}}
                )
                Write-Output $item.StorageQuotaUsage|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionComputeQuotaUsage
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if($item.ComputeQuotaUsage -ne $null)
            {
                $SelectScope=@('Unit','CurrentValue','Limit','Location'
                    @{N='Name';E={$_.Name.Value}}
                    @{N='LocalizedName';E={$_.Name.LocalizedValue}}
                )
                Write-Output $item.ComputeQuotaUsage|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionMetricSet
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            #Group by resource
            $ResourceMetricGroups=$item.MetricValues|Sort-Object -Property TimeStamp -Descending|Group-Object -Property ResourceId
            foreach ($ResourceMetricGroup in $ResourceMetricGroups)
            {
                Write-Verbose "Resource $($ResourceMetricGroup.Name)"
                $MetricTypeGroups=$ResourceMetricGroup.Group|Group-Object {$_.Name.Value}
                foreach ($MetricTypeGroup in $MetricTypeGroups)
                {
                    Write-Verbose "Metric $($MetricTypeGroup.Name)"
                    $SelectScope=@('ResourceId','TimeStamp','ResourceType'
                        @{N='LocalizedMetricName';E={$_.Name.LocalizedValue}},
                        @{N='MetricName';E={$_.Name.Value}},
                        'MetricValue'
                    )
                    Write-Output $MetricTypeGroup.Group|Select-Object -Property $SelectScope
                }
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionRecommendations
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $SelectScope=@('Name',
                @{'N'='Category';'E'={$_.Properties.Category}},
                @{'N'='Impact';'E'={$_.Properties.Impact}},
                @{'N'='ResourceType';'E'={$_.Properties.ImpactedField}},
                @{'N'='ResourceName';'E'={$_.Properties.ImpactedValue}},
                @{'N'='Risk';'E'={$_.Properties.Risk}},
                @{'N'='Problem';'E'={$_.Properties.ShortDescription.Problem}},
                @{'N'='Solution';'E'={$_.Properties.ShortDescription.Solution}}
            )
            Write-Output $item.AdvisorRecommendations|Select-Object -Property $SelectScope
        }
    }
    END
    {

    }
}

Function Export-SubscriptionEventlog
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if($item.EventLogEntries -ne $null)
            {
                $SelectScope=@('EventTimestamp','SubmissionTimestamp','SubscriptionId',
                    'Level','CorrelationId','EventDataId','OperationId','Caller'
                    'Id','ResourceUri','Channels','ResourceGroupName',
                    @{'N'='ResourceProviderName';'E'={$_.ResourceProviderName.Value}},
                    @{'N'='EventName';'E'={$_.EventName.Value}},
                    @{'N'='EventLocalizedName';'E'={$_.EventName.LocalizedValue}},
                    @{'N'='EventSource';'E'={$_.EventSource.Value}},
                    @{'N'='EventLocalizedSource';'E'={$_.EventSource.LocalizedValue}},
                    @{'N'='OperationName';'E'={$_.OperationName.Value}},
                    @{'N'='OperationLocalizedName';'E'={$_.OperationName.LocalizedValue}},
                    @{'N'='LocalizedStatus';'E'={$_.Status.LocalizedValue}},
                    @{'N'='Status';'E'={$_.Status.Value}},
                    @{'N'='LocalizedSubStatus';'E'={$_.SubStatus.LocalizedValue}},
                    @{'N'='SubStatus';'E'={$_.SubStatus.Value}},
                    @{'N'='HttpClientRequestId';'E'={$_.HttpRequest.ClientRequestId}},
                    @{'N'='HttpClientIpAddress';'E'={$_.HttpRequest.ClientIpAddress}},
                    @{'N'='HttpMethod';'E'={$_.HttpRequest.Method}}
                )
                Write-Output $item.EventLogEntries|Sort-Object -Property 'EventTimestamp'|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionUsageAggregates
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if ($item.UsageAggregates -ne $null)
            {
                $SelectScope=@('SubscriptionId','MeterId','MeterCategory',
                   'MeterName','MeterSubCategory','MeterRegion','Unit','Quantity',
                   @{N='Project';E={$_.InfoFields.Project}}
                )
                Write-Output $item.UsageAggregates|Select-Object -ExpandProperty 'Properties'|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionRateCard
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if($item.RateCard -ne $null)
            {
                $SelectScope=@('MeterId','MeterName','EffectiveDate',
                    'IncludedQuantity','MeterCategory','MeterRegion',
                    'MeterSubCategory','MeterTags','Unit',
                    @{
                        N='MeterRates';
                        E={$_.MeterRates|ForEach-Object{($_|Get-Member -MemberType NoteProperty|Select-Object -ExpandProperty Definition).Replace('decimal ','')}};
                    }
                )
                Write-Output $item.RateCard.Meters|Sort-Object -Property EffectiveDate|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionRoleDefinitions
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if ($item.RoleDefinitions -ne $null)
            {
                $SelectScope=@('Id','Name',
                    @{N="Description";E={$_.Properties.Description}},
                    @{N="RoleName";E={$_.Properties.RoleName}},
                    @{N="Type";E={$_.Properties.Type}},
                    @{N="CreatedOn";E={$_.Properties.CreatedOn}},
                    @{N="CreatedBy";E={$_.Properties.CreatedBy}},
                    @{N="UpdatedOn";E={$_.Properties.UpdatedOn}},
                    @{N="UpdatedBy";E={$_.Properties.UpdatedBy}},
                    @{N="AssignableScopes";E={$_.Properties.AssignableScopes}},
                    @{N="Actions";E={$_.Properties.Actions}},
                    @{N="NonActions";E={$_.Properties.NonActions}}
                )
                Write-Output $item.RoleDefinitions|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionRoleAssignments
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if ($item.RoleAssignments -ne $null -and $item.RoleDefinitions -ne $null)
            {
                $RoleDefinitionSet=$item.RoleDefinitions|Select-Object -Property @('Id',
                        @{N='Name';E={$_.Properties.RoleName}},
                        @{N='Description';E={$_.Properties.Description}},
                        @{N='Type';E={$_.Properties.Type}}
                )
                $SelectScope=@('Id','Name',
                    @{N="RoleDefinitionId";E={$_.Properties.RoleDefinitionId}},
                    @{N="PrincipalId";E={$_.Properties.PrincipalId}},
                    @{N="Scope";E={$_.Properties.Scope}},
                    @{N="CreatedOn";E={$_.Properties.CreatedOn}},
                    @{N="CreatedBy";E={$_.Properties.CreatedBy}},
                    @{N="UpdatedOn";E={$_.Properties.UpdatedOn}},
                    @{N="UpdatedBy";E={$_.Properties.UpdatedBy}},
                    @{N='RoleType';E={$RoleDefinitionSet|Where-Object Id -eq $_.Properties.RoleDefinitionId|Select-Object -ExpandProperty Type}},
                    @{N='RoleName';E={$RoleDefinitionSet|Where-Object Id -eq $_.Properties.RoleDefinitionId|Select-Object -ExpandProperty Name}},
                    @{N='RoleDescription';E={$RoleDefinitionSet|Where-Object Id -eq $_.Properties.RoleDefinitionId|Select-Object -ExpandProperty Description}}
                )
                Write-Output $item.RoleAssignments|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionPolicyDefinitions
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if ($item.PolicyDefinitions -ne $null)
            {
                $SelectScope=@('Id','Name',
                    @{N="DisplayName";E={$_.Properties.DisplayName}},
                    @{N="Description";E={$_.Properties.Description}},
                    @{N="PolicyType";E={$_.Properties.PolicyType}}
                )
                Write-Output $item.PolicyDefinitions|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionPolicyAssignments
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            $PolicyDefinitions=$item.PolicyDefinitions|Select-Object -Property @('Id','Name',
                    @{N="DisplayName";E={$_.Properties.DisplayName}},
                    @{N="Description";E={$_.Properties.Description}},
                    @{N="PolicyType";E={$_.Properties.PolicyType}}
                )
            if($item.PolicyAssignments -ne $null -and $item.PolicyDefinitions -ne $null)
            {
                $SelectScope=@('Id','Name',
                    @{N="Scope";E={$_.Properties.Scope}},
                    @{N="PolicyDefinitionId";E={$_.Properties.PolicyDefinitionId}},
                    @{N="PolicyName";E={$PolicyDefinitions|Where-Object Id -eq $_.Properties.PolicyDefinitionId|Select-Object -ExpandProperty DisplayName}},
                    @{N="PolicyDescription";E={$PolicyDefinitions|Where-Object Id -eq $_.Properties.PolicyDefinitionId|Select-Object -ExpandProperty Description}},
                    @{N="PolicyType";E={$PolicyDefinitions|Where-Object Id -eq $_.Properties.PolicyDefinitionId|Select-Object -ExpandProperty PolicyType}}
                )
                Write-Output $item.PolicyAssignments|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionResourceLocks
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if($item.ResourceLocks -ne $null)
            {
                $SelectScope=@('Id','Name',
                    @{N="Level";E={$_.Properties.Level}},
                    @{N="Notes";E={$_.Properties.Notes}}
                )
                Write-Output $item.ResourceLocks|Select-Object -Property $SelectScope
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionTagNameReport
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if($item.TagNameReport -ne $null)
            {
                foreach ($TagNameItem in $item.TagNameReport)
                {
                    foreach ($TagValue in $TagNameItem.Values)
                    {
                        $SelectScope=@(
                            'TagValue',
                            @{N="ValueCountType";E={$_.Count.Type}},
                            @{N="ValueCountValue";E={$_.Count.Value}},
                            @{N="TagNameId";E={$TagNameItem.Id}},
                            @{N="TagName";E={$TagNameItem.TagName}},
                            @{N="NameCountType";E={$TagNameItem.Count.Type}},
                            @{N="NameCountValue";E={$TagNameItem.Count.Value}}
                        )
                        Write-Output $TagValue|Select-Object -Property $SelectScope
                    }
                }
            }
        }
    }
    END
    {

    }
}

Function Export-SubscriptionVmSizes
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [SubscriptionSummary[]]
        $Summary
    )
    BEGIN
    {

    }
    PROCESS
    {
        foreach ($item in $Summary)
        {
            if($item.AvailableVmSizes -ne $null)
            {
                Write-Output $item.AvailableVmSizes
            }
        }
    }
    END
    {

    }
}

#endregion

#endregion

Function Get-AzureDetailReport
{
    [CmdletBinding(ConfirmImpact='None')]
    param
    (
        [Parameter(Mandatory=$true)]
        [String[]]
        $TenantId,
        [Parameter(Mandatory=$true)]
        [String]
        $GraphAccessToken,
        [Parameter(Mandatory=$true)]
        [String]
        $ArmAccessToken,
        [Parameter(Mandatory=$false)]
        [System.Uri]
        $ArmFrontDoorUri='https://management.azure.com',
        [Parameter(Mandatory=$true)]
        [String]
        $OfferId,
        [Parameter(Mandatory=$true)]
        [datetime]
        $End,
        [Parameter(Mandatory=$true)]
        [datetime]
        $Start,
        [Parameter(Mandatory=$false)]
        [String]
        $Region="US",
        [Parameter(Mandatory=$false)]
        [String]
        $Locale="en-US",
        [ValidateSet('Average','Maximum','Minimum','Total')]
        [Parameter(Mandatory=$false)]
        [String]
        $MetricAggregationType="Average",
        [Parameter(Mandatory=$false)]
        [String]
        $MetricGranularity="PT1H",
        [ValidateSet('Hourly','Daily')]
        [Parameter(Mandatory=$false)]
        [String]
        $UsageGranularity="Hourly",
        [Parameter(Mandatory=$false)]
        [Switch]
        $Usage,
        [Parameter(Mandatory=$false)]
        [Switch]
        $Metrics,
        [Parameter(Mandatory=$false)]
        [Switch]
        $InstanceData,
        [Parameter(Mandatory=$false)]
        [ScriptBlock]
        $SubscriptionFilter={$_.DisplayName -ne 'Access To Azure Active Directory'},
        [Parameter(Mandatory=$false)]
        [Switch]
        $TenantEvents,
        [Parameter(Mandatory=$false)]
        [Switch]
        $OauthPermissionGrants,
        [Parameter(Mandatory=$false)]
        [Switch]
        $ServicePrincipals,
        [Parameter(Mandatory=$false)]
        [Switch]
        $Applications,
        [Parameter(Mandatory=$false)]
        [Switch]
        $ResourcesOnly,
        [Parameter(Mandatory=$false)]
        [Switch]
        $TenantOnly
    )

    $Report=[DetailReport]::new()

    if ($TenantOnly.IsPresent -or $ResourcesOnly.IsPresent -eq $false) {
        #Get the Tenant Summaries
        [TenantSummary[]]$TenantSummaries=@(Get-TenantSummary -TenantId $TenantId -AccessToken $GraphAccessToken `
            -Start $Start -End $End `
            -Events:$TenantEvents.IsPresent `
            -OAuthPermissionGrants:$OAuthPermissionGrants.IsPresent `
            -Applications:$Applications.IsPresent `
            -ServicePrincipals:$ServicePrincipals.IsPresent
        )
        $Report.Summaries+=$TenantSummaries
    }
    if($ResourcesOnly.IsPresent -or $TenantOnly.IsPresent -eq $false)
    {
        #Get the Subscription Summaries
        [SubscriptionSummary[]]$SubscriptionSummaries=@(Get-SubscriptionSummary -AccessToken $ArmAccessToken `
            -OfferId $OfferId -End $End -Start $Start `
            -Region $Region -Locale $Locale `
            -AggregationType $MetricAggregationType -MetricGranularity $MetricGranularity `
            -UsageGranularity $UsageGranularity -InstanceData:$InstanceData.IsPresent `
            -Usage:$Usage.IsPresent -Metrics:$Metrics.IsPresent `
            -ArmFrontDoorUri $ArmFrontDoorUri -SubscriptionFilter $SubscriptionFilter
        )
        $Report.Summaries+=$SubscriptionSummaries        
    }
    Write-Output $Report
}