BillingSqlDb.psm1


# Instal Pre-Req Modules

Invoke-InstallOrUpdateModule 'SqlServer'

function Get-StagingTableRecordsCount {
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Billing db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Switch]$exportCsv)

    $query = 
            "select min(starttime) as MinStartTime, count(1) as RecordsCount, 'Staging Hourly' as TableName from Staging_BillingHourlyRecords with (nolock)
             select min(starttime) as MinStartTime, count(1) as RecordsCount, 'Staging Daily' as TableName from Staging_BillingDailyRecords with (nolock)
             select min(starttime) as MinStartTime, count(1) as RecordsCount, 'Staging Monthly' as TableName from Staging_BillingMonthlyRecords with (nolock)
             select min(starttime) as MinStartTime, count(1) as RecordsCount, 'Staging Monthly Resource Summary' as TableName from Staging_BillingMonthlyResourceSummaries with (nolock)
             select min(starttime) as MinStartTime, count(1) as RecordsCount, 'Staging Monthly Running Summary' as TableName from Staging_BillingMonthlyRunningSummary with (nolock)"

    try
    {
        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
        Write-Host "Staging table data result: "
        $result.Tables

        if($exportCsv) 
        {
            $dateTimeTicks = (Get-Date).Ticks
            $outFileName = 'StagingResults_' + $dateTimeTicks + '.csv'
            Write-Host "Writing to File: $outFileName"
            foreach($table in $result.Tables) 
            {
                $table | Export-Csv -Path $outFileName -NoTypeInformation –Append
            }
        }
    }
    catch {
        $exception = $_
        Write-Host "Error getting staging table details"
        Write-Error $exception
    }
}

function ResetBillingUsages {
 param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Billing db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $true, Position = 1, HelpMessage = "Specifiy start time from when the reset has to be done")]
        $startDateTime,
        [Parameter(Mandatory = $true, Position = 2)]
        $costManagementDBName,
        [Parameter(Mandatory = $false, Position = 3)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 4)]
        $timeOut,
        [Switch]$exportCsv)

    $query = 
        "DECLARE @lastprocessedtime NVARCHAR(30)
 
        SET @lastprocessedtime = '$startDateTime'
        UPDATE [dbo].[UsageProcessingStates]
            SET [LastProcessedTime] = @lastprocessedtime
            WHERE StateName = 'BillingMonthlySummaryAggregator'
 
        UPDATE [dbo].[UsageProcessingStates]
            SET [LastProcessedTime] = @lastProcessedTime,
                [LastSeenTime] = @lastProcessedTime
            WHERE StateName = 'DailyTenantCostReportGenerator'
 
        UPDATE [dbo].[BillingMonthlyResourceSummaries]
            SET IsProcessed = 0
            WHERE
            (
                CreatedTime > (SELECT InvoiceCreatedTime FROM BillingInvoices WHERE InvoiceStartTime = @lastprocessedtime) AND
                StartTime < @lastprocessedtime
            )
 
        DELETE FROM [dbo].[RawUsageRecordProcessingStatus] WHERE RecordId IN (SELECT ID FROM [dbo].[RawUsageRecords] WHERE StartTime >= @lastprocessedtime)
 
        DELETE FROM [dbo].[Staging_BillingHourlyRecords] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[Staging_BillingDailyRecords] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[Staging_BillingMonthlyRecords] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[Staging_BillingMonthlyResourceSummaries] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[Staging_BillingMonthlyRunningSummary] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingHourlyRecords] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingDailyRecords] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingInvoiceSendInfos]
        DELETE FROM [dbo].[BillingInvoiceLineItems] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingInvoices] WHERE InvoiceStartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingUserInvoiceLineItems] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingUserInvoices] WHERE InvoiceStartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingMonthlyRecords] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingMonthlyResourceSummaries] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingMonthlyRunningSummary] WHERE StartTime >= @lastProcessedTime
        DELETE FROM [dbo].[BillingHistory] WHERE StartTime >= @lastProcessedTime
        DELETE FROM[dbo].[DailyTenantCostReport] WHERE StartTime >= @lastProcessedTime
        DELETE FROM[dbo].[DashboardDailyCostSummary] WHERE StartTime >= @lastProcessedTime
 
        ---------CSP License-------
        UPDATE [dbo].[UsageProcessingStates]
            SET [LastProcessedTime] = @lastprocessedtime
            WHERE StateName = 'CspLicenseLogProcessor'
 
        DELETE from [dbo].[CspLicensePurchaseLogRecordProcessingStatus] WHERE UsageStartTime >= @lastprocessedtime
        ---------------
 
        --DELETE WRT to lastProcessedTime, get the external record id from Cost Management from which reset must be performed.
        --Update the Cost Management database name in belo queries.
        DECLARE @lastAwsCostRecordId INT
        SELECT TOP 1 @lastAwsCostRecordId = RecordId FROM [$costManagementDBName].Source.AwsCurSourceData
            WHERE LineItem_UsageStartDate >= @lastProcessedTime ORDER BY RecordId
        DELETE from [dbo].[AwsCostRecordProcessingStatus] WHERE RecordId >= @lastAwsCostRecordId
 
        DECLARE @lastAzsHubCostRecordId INT
        SELECT TOP 1 @lastAzsHubCostRecordId = RecordId FROM [$costManagementDBName].Source.AzsHubUsageData
            WHERE UsageStartTime >= @lastProcessedTime ORDER BY RecordId
        DELETE from [dbo].[AzsHubUsageRecordProcessingStatus] WHERE RecordId >= @lastAzsHubCostRecordId
 
        DECLARE @lastCspAzureCostRecordId INT
        SELECT TOP 1 @lastCspAzureCostRecordId = RecordId FROM [$costManagementDBName].Source.CspAzureInvoiceLineItems
            WHERE ChargeStartDate >= @lastProcessedTime ORDER BY RecordId
        DELETE from [dbo].[CspCostRecordProcessingStatus] WHERE RecordId >= @lastCspAzureCostRecordId
 
        DECLARE @lastCspLicenseCostRecordId INT
        SELECT TOP 1 @lastCspLicenseCostRecordId = RecordId FROM [$costManagementDBName].Source.CspLicenseInvoiceLineItems
            WHERE ChargeStartDate >= @lastProcessedTime ORDER BY RecordId
        DELETE from [dbo].CspLicenseCostRecordProcessingStatus WHERE RecordId >= @lastCspAzureCostRecordId
 
        DECLARE @lastCspEstimateCostRecordId INT
        SELECT TOP 1 @lastCspEstimateCostRecordId = RecordId FROM [$costManagementDBName].[Source].[CspAzureUnBilledLineItems]
            WHERE ChargeStartDate >= @lastProcessedTime ORDER BY RecordId
        DELETE from [dbo].CspCostEstimateRecordProcessingStatus WHERE RecordId >= @lastCspEstimateCostRecordId"


     try
     {
        $sqlDbConn = Get-SqlDbConnection $connectionName
        $decoded = Invoke-Decode $sqlDbConn.Password
        if($dbName -eq $null) {
            $dbName = $sqlDbConn.DbName
        }

        if($timeOut -eq $null) {
            $timeOut = 1800
        }

        Write-Host "Executing reset query.."

        $data = Invoke-Sqlcmd -ServerInstance $sqlDbConn.DbServerName -Database $dbName -Username $sqlDbConn.User -Password "$decoded" -Query $query -OutputAs DataSet -QueryTimeout $timeOut
        
        Write-Host "Usage Reset completed. Validating if there are any monthly data after $startDateTime"

        $validateMonthlyRecords = "select count(1) from BillingMonthlyRecords with (nolock) where [StartTime] >= '$startDateTime'"
        $validationResult = Invoke-SqlDbQuery -name $connectionName -query $validateMonthlyRecords -dbName $dbName -printToConsole

        if ($validationResult.Column1 -eq 0)
        {
            Write-Host "Reset successful. There are no data found after $startDateTime"
        }
        else
        {
            Write-Host "Reset failed. There are "$validationResult.Column1" records in monthly table after reset for the specified period. Kindly execute reset again."
        }
     }
     catch 
     {
        $exception = $_
        Write-Host "Error resetting usage"
        Write-Error $exception
     }
}

function Invoke-ClearStagingTable {
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Billing db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1, HelpMessage = "Records of specified and previous months will be cleared. If no date is provided, current month records will not be cleared")]
        $resetTillDate,
        [Parameter(Mandatory = $false, Position = 2)]
        $dbName,
        [Switch]$exportResult)

    if (!$resetTillDate) {
        $resetTillDate = (Get-Date).AddMonths(-1).tostring("yyyy-MM-01")
    }
        
    $query = "DELETE from Staging_BillingHourlyRecords where [StartTime] <= '$resetTillDate'; SELECT @@ROWCOUNT AS DeletedHourlyRows
              DELETE from Staging_BillingDailyRecords where [StartTime] <= '$resetTillDate'; SELECT @@ROWCOUNT AS DeletedDailyRows
              DELETE from Staging_BillingMonthlyRecords where [StartTime] <= '$resetTillDate'; SELECT @@ROWCOUNT AS DeletedMonthlyRows
              DELETE from Staging_BillingMonthlyResourceSummaries where [StartTime] <= '$resetTillDate'; SELECT @@ROWCOUNT AS DeletedMonthlyResourceSummaryRows
              DELETE from Staging_BillingMonthlyRunningSummary where [StartTime] <= '$resetTillDate'; SELECT @@ROWCOUNT AS DeletedMonthlyRunningSummaryRows"

    
    try
    {
        Write-Host "Stopping Billing Agent Service..."
        net stop BillingAgentService

        Write-Host "`nClearing staging table records..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
        Write-Host "`nSuccessfully cleared staging records"

        foreach($table in $result.Tables) 
        {
            $outData = $outData + "`n" + $table.Columns.Caption + ": " + $table.ItemArray
        }

        Write-Host "`nResults:"
        $outData

        if($exportResult) 
        {
            $dateTimeTicks = (Get-Date).Ticks
            $outFileName = '.\ClearStagingResults_' + $dateTimeTicks + '.txt'
            Write-Host "Writing to File: $outFileName"
            $outData | Out-File -FilePath $outFileName
        }
    }
    catch {
        $exception = $_
        Write-Host "Error clearing staging table records"
        Write-Error $exception
    }
    finally {
        Write-Host "`nStarting Billing Agent Service..."
        net start BillingAgentService
    }
}

function Get-UsageQuotaData {
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "DACM db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 2)]
        $numberOfRecords)

    try
    {
        if(!$numberOfRecords) {
            $numberOfRecords = 100
        }

        $aggregationTableQueries = "select MAX(StartTime) as MaxStartTime, 'Hourly' as TableName from Source.AzsHubQuotaHourlyRecords with (nolock)
                                    select MAX(StartTime) as MaxStartTime, 'Daily' as TableName from Source.AzsHubQuotaDailyRecords with (nolock)
                                    select MAX(StartTime) as MaxStartTime, 'Monthly' as TableName from Source.AzsHubQuotaMonthlyRecords with (nolock)"


        $hourlyStatus = "select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                         where [Processor] = 'AzsHubQuotaCollector'
                         order by [LastUpdatedTime] desc"

        $dailyStatus = "select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                         where [Processor] = 'AzsHubQuotaDailyAggregator'
                         order by [LastUpdatedTime] desc"

        $monthlyStatus = "select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                         where [Processor] = 'AzsHubQuotaMonthlyAggregator'
                         order by [LastUpdatedTime] desc"


        $subscriptionStatus = "select * from Pipeline.SubscriptionProcessingStatus with (nolock)
                               where [PlatformType] = 'AzsHubQuota'"

                                       
        $dateTimeTicks = (Get-Date).Ticks
        $directoryName = 'UsageQuotaData'
        If(!(test-path $directoryName))
        {
            New-Item -ItemType Directory -Force -Path $directoryName
        }

        Write-Host "Getting Quota Aggregation tables data..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $aggregationTableQueries -dbName $dbName -printToConsole
        $outFileName = $directoryName +'\AggregationTableResults_' + $dateTimeTicks + '.csv'
        $result.Tables
        Write-ResultDataToCSV -result $result.Tables -outFileName $outFileName

        Write-Host "Getting Quota Hourly Status data..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $hourlyStatus -dbName $dbName -printToConsole
        $outFileName = $directoryName +'\HourlyStatusTableResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName

        Write-Host "Getting Quota Daily Status data..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $dailyStatus -dbName $dbName -printToConsole
        $outFileName = $directoryName +'\DailyStatusTableResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName

        Write-Host "Getting Quota Monthly Status data..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $monthlyStatus -dbName $dbName -printToConsole
        $outFileName = $directoryName +'\MonthlyStatusTableResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName

        Write-Host "Getting Subscription Status data..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $subscriptionStatus -dbName $dbName -printToConsole
        $outFileName = $directoryName +'\SubscriptionStatusResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName
    }
    catch {
        $exception = $_
        Write-Host "Error getting Usage and Quota table details"
        Write-Error $exception
    }
}

function Get-UsageQuotaFilterData{
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "DACM db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName)
        
    try
    {
        $syncEnabledSubscriptions = "SELECT [ConnectionId], [SubscriptionId], Connections.[Name] AS ConnectionName, [Owner]
                                     FROM Source.AzsHubSubscriptions AS Subscriptions
                                     INNER JOIN Config.Connections AS Connections
                                     ON Subscriptions.[ConnectionId] = Connections.[Id]
                                     WHERE [IsSyncEnabled] = 1 AND [AdditionalInfoJson] IS NOT NULL"


        $distinctResources = "Select distinct [ResourceDisplayName] as Resource, [ServiceProvider] from Source.AzsHubQuotaMonthlyRecords with (nolock)
                              where [StartTime] >= (select max(StartTime) from source.AzsHubQuotaMonthlyRecords with (nolock))"

        
        $dateTimeTicks = (Get-Date).Ticks
        $directoryName = 'UsageQuotaData'
        If(!(test-path $directoryName))
        {
            New-Item -ItemType Directory -Force -Path $directoryName
        }

        Write-Host "Getting sync enabled subscription details..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $syncEnabledSubscriptions -dbName $dbName -printToConsole
        $outFileName = $directoryName + '\SynSubscriptionResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName

        Write-Host "Getting distinct providers and respective resource type..."
        $resourceResult = Invoke-SqlDbQuery -name $connectionName -query $distinctResources -dbName $dbName -printToConsole
        $outFileName = $directoryName + '\ProviderAndResourceTypeResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $resourceResult -outFileName $outFileName
    }
    catch {
        $exception = $_
        Write-Host "Error getting Usage and quota filters"
        Write-Error $exception
    }
}

function Get-AppSettings{
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Billing db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 2)]
        $key,
        [Parameter(Mandatory = $true, Position = 3)]
        $group,
        [Switch]$exportCsv)
    try
    {
        $query = "select * from AppSettings with (nolock) where [Group] = '$group'"

        if ($key)
        {
            $query = $query + " and [Key] = '$key'"
        }

        Write-Host "Getting AppSettings..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
        $result

        if ($exportCsv)
        {
            $dateTimeTicks = (Get-Date).Ticks
            $outFileName = '.\AppSettingsResult_' + $dateTimeTicks + '.csv'
            Write-Host "Writing to File: $outFileName"
            Write-ResultDataToCSV -result $result -outFileName $outFileName
        }
    }
    catch {
        $exception = $_
        Write-Host "Error getting AppSettings"
        Write-Error $exception
    }
}

function Update-AppSettings{
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Billing db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Parameter(Mandatory = $true, Position = 2)]
        $key,
        [Parameter(Mandatory = $true, Position = 3)]
        $group,
        [Parameter(Mandatory = $true, Position = 3)]
        $value)
    try
    {
        $query = "update AppSettings set [Value] = '$value' where [Group] = '$group' and [Key] = '$key'; SELECT @@ROWCOUNT AS UpdatedRows"

        Write-Host "Updating AppSettings..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole

        if ($result.UpdatedRows -gt 1)
        {
            Write-Host "More than one row updated."
        }
        elif ($result.UpdatedRows -eq 0)
        {
            Write-Host "No AppSetting got updated."
        }
        else
        {
            Write-Host "Successfully update the AppSetting."
        }
    }
    catch {
        $exception = $_
        Write-Host "Error updating AppSettings"
        Write-Error $exception
    }
}

function Get-SubscriptionQuotaStatusReport {
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "DACM db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 2)]
        $year,
        [Parameter(Mandatory = $false, Position = 3)]
        $month)

    try
    {
        if (!$year -or !$month)
        {
            $startTime = (Get-Date).tostring("yyyy-MM-01 00:00:00.000")
            $endTime = (Get-Date -Day 01).AddMonths(1).AddDays(-1).tostring("yyyy-MM-dd 23:59:59.997")
        }
        else
        {
            $startTime = "$year-$month-01 00:00:00.000"
            $endTime = [datetime]::parseexact("$year-$month-01", 'yyyy-MM-dd', $null).AddMonths(1).AddDays(-1).tostring("yyyy-MM-dd 23:59:59.997")
        }

        $dateTimeTicks = (Get-Date).Ticks
        $directoryName = 'UsageQuotaReportData'
        If(!(test-path $directoryName))
        {
            New-Item -ItemType Directory -Force -Path $directoryName
        }

        $query = "Select SubscriptionId, SubscriptionName, ConnectionName, UserId, OK, Critical, Warning From Pipeline.ServiceResourceReportData with (nolock)
                  CROSS Apply OPENJSON(ReportData)
                  With(
                        SubscriptionId VARCHAR(60),
                        SubscriptionName VARCHAR(50),
                        ConnectionName VARCHAR(50),
                        UserId VARCHAR(50),
                        OK INT,
                        Critical INT,
                        Warning INT
                  )
                  WHERE
                  StartTime >= '$startTime'
                  AND EndTime <= '$endTime'
                  AND LookupId IN (Select Id From Pipeline.CommonServiceResourceLookups with (nolock)
                              Where ResourceType = 'SubscriptionQuotaStatus')"


        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
        Write-Host "Number of records for " $startTime " : " $result.Count
        $outFileName = $directoryName + '\SubscriptionQuotaStatusResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName
    }
    catch {
        $exception = $_
        Write-Host "Error getting Usage and Quota Subscription Quota status report"
        Write-Error $exception
    }
}

function Get-QuotaConsumptionReport {
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "DACM db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 2)]
        $year,
        [Parameter(Mandatory = $false, Position = 3)]
        $month)

    try
    {
        if (!$year -or !$month)
        {
            $startTime = (Get-Date).tostring("yyyy-MM-01 00:00:00.000")
            $endTime = (Get-Date -Day 01).AddMonths(1).AddDays(-1).tostring("yyyy-MM-dd 23:59:59.997")
        }
        else
        {
            $startTime = "$year-$month-01 00:00:00.000"
            $endTime = [datetime]::parseexact("$year-$month-01", 'yyyy-MM-dd', $null).AddMonths(1).AddDays(-1).tostring("yyyy-MM-dd 23:59:59.997")
        }

        $dateTimeTicks = (Get-Date).Ticks
        $directoryName = 'UsageQuotaReportData'
        If(!(test-path $directoryName))
        {
            New-Item -ItemType Directory -Force -Path $directoryName
        }

        $query = "SELECT lookup.ResourceType,lookup.SubscriptionId,report.ReportData
                  FROM Pipeline.ServiceResourceReportData as report with (nolock)
                  inner join Pipeline.CommonServiceResourceLookups as lookup with (nolock)
                  on report.LookupId = lookup.Id
                  WHERE
                  report.StartTime >= '$startTime'
                  AND report.EndTime <= '$endTime'
                  AND lookup.ResourceType in ('StampQuotaConsumption','SubscriptionQuotaConsumption')"


        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
        Write-Host "Number of records for " $startTime " : " $result.Count
        $outFileName = $directoryName + '\QuotaConsumptionResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName
    }
    catch {
        $exception = $_
        Write-Host "Error getting Usage and Quota consumption report"
        Write-Error $exception
    }
}

function Get-TrendReport {
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "DACM db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 2)]
        $year,
        [Parameter(Mandatory = $false, Position = 3)]
        $month)

    try
    {
        if (!$year -or !$month)
        {
            $startTime = (Get-Date).tostring("yyyy-MM-01 00:00:00.000")
            $endTime = (Get-Date -Day 01).AddMonths(1).AddDays(-1).tostring("yyyy-MM-dd 23:59:59.997")
        }
        else
        {
            $startTime = "$year-$month-01 00:00:00.000"
            $endTime = [datetime]::parseexact("$year-$month-01", 'yyyy-MM-dd', $null).AddMonths(1).AddDays(-1).tostring("yyyy-MM-dd 23:59:59.997")
        }

        $dateTimeTicks = (Get-Date).Ticks
        $directoryName = 'UsageQuotaReportData'
        If(!(test-path $directoryName))
        {
            New-Item -ItemType Directory -Force -Path $directoryName
        }

        $query = "Select QuotaProvider, QuotaResource, MaxQuota, ConsumedQuota, ConsumptionPercentage, UsageDate From Pipeline.ServiceResourceReportData
                  CROSS Apply OPENJSON(ReportData)
                  With(
                        QuotaProvider VARCHAR(60),
                        QuotaResource VARCHAR(50),
                        UsageDate DATETIME,
                        MaxQuota VARCHAR(100),
                        ConsumedQuota VARCHAR(100),
                        ConsumptionPercentage VARCHAR(100)
                  )
                  WHERE
                  StartTime >= '$startTime'
                  AND EndTime <= '$endTime'"


        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
        Write-Host "Number of records for " $startTime " : " $result.Count
        $outFileName = $directoryName + '\TrendReportResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName
    }
    catch {
        $exception = $_
        Write-Host "Error getting Usage and Quota consumption report"
        Write-Error $exception
    }
}

function Get-AzsReportStatus{
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "DACM db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 2)]
        $numberOfRecords)
        
    try
    {
        if(!$numberOfRecords) {
            $numberOfRecords = 100
        }

        $reportTableQueries = "Select MAX(StartTime) as MaxStartTime, 'StampQuotaConsumption' as ResourceType From Pipeline.ServiceResourceReportData with (nolock)
                               where [LookupId] in (select Id from Pipeline.CommonServiceResourceLookups with (nolock)
                               where [ResourceType] = 'StampQuotaConsumption')
 
                               Select MAX(StartTime) as MaxStartTime, 'SubscriptionQuotaConsumption' as ResourceType From Pipeline.ServiceResourceReportData with (nolock)
                               where [LookupId] in (select Id from Pipeline.CommonServiceResourceLookups with (nolock)
                               where [ResourceType] = 'SubscriptionQuotaConsumption')
 
                               Select MAX(StartTime) as MaxStartTime, 'SubscriptionQuotaStatus' as ResourceType From Pipeline.ServiceResourceReportData with (nolock)
                               where [LookupId] in (select Id from Pipeline.CommonServiceResourceLookups with (nolock)
                               where [ResourceType] = 'SubscriptionQuotaStatus')
 
                               Select MAX(StartTime) as MaxStartTime, 'AllStampsQuotaWeekReport' as ResourceType From Pipeline.ServiceResourceReportData with (nolock)
                               where [LookupId] in (select Id from Pipeline.CommonServiceResourceLookups with (nolock)
                               where [ResourceType] = 'AllStampsQuotaWeekReport')
 
                               Select MAX(StartTime) as MaxStartTime, 'AllStampsQuotaMidMonthReport' as ResourceType From Pipeline.ServiceResourceReportData with (nolock)
                               where [LookupId] in (select Id from Pipeline.CommonServiceResourceLookups with (nolock)
                               where [ResourceType] = 'AllStampsQuotaMidMonthReport')
 
                               Select MAX(StartTime) as MaxStartTime, 'AllStampsQuotaMonthReport' as ResourceType From Pipeline.ServiceResourceReportData with (nolock)
                               where [LookupId] in (select Id from Pipeline.CommonServiceResourceLookups with (nolock)
                               where [ResourceType] = 'AllStampsQuotaMonthReport')
 
                               Select MAX(StartTime) as MaxStartTime, 'SubscriptionQuotaWeekReport' as ResourceType From Pipeline.ServiceResourceReportData with (nolock)
                               where [LookupId] in (select Id from Pipeline.CommonServiceResourceLookups with (nolock)
                               where [ResourceType] = 'SubscriptionQuotaWeekReport')
 
                               Select MAX(StartTime) as MaxStartTime, 'SubscriptionQuotaMidMonthReport' as ResourceType From Pipeline.ServiceResourceReportData with (nolock)
                               where [LookupId] in (select Id from Pipeline.CommonServiceResourceLookups with (nolock)
                               where [ResourceType] = 'SubscriptionQuotaMidMonthReport')
 
                               Select MAX(StartTime) as MaxStartTime, 'SubscriptionQuotaMonthReport' as ResourceType From Pipeline.ServiceResourceReportData with (nolock)
                               where [LookupId] in (select Id from Pipeline.CommonServiceResourceLookups with (nolock)
                               where [ResourceType] = 'SubscriptionQuotaMonthReport')"


        $statusQuries = "select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                         where [Processor] = 'AzsHubReportCsrlLookupProcessor'
                         order by [LastUpdatedTime] desc
                         select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                         where [Processor] = 'AzsHubSubscriptionQuotaStatusProcessor'
                         order by [LastUpdatedTime] desc
                         select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                         where [Processor] = 'AzsHubDailyReportProcessor'
                         order by [LastUpdatedTime] desc"


        $dateTimeTicks = (Get-Date).Ticks
        $directoryName = 'UsageQuotaReportData'
        If(!(test-path $directoryName))
        {
            New-Item -ItemType Directory -Force -Path $directoryName
        }

        Write-Host "Getting Quota Report tables data..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $reportTableQueries -dbName $dbName -printToConsole
        $outFileName = $directoryName +'\ReportTableResults_' + $dateTimeTicks + '.csv'
        $result.Tables
        Write-ResultDataToCSV -result $result.Tables -outFileName $outFileName

        Write-Host "Getting Quota Report Status data..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $statusQuries -dbName $dbName -printToConsole
        $outFileName = $directoryName +'\QuotaReportStatusTableResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result.Tables -outFileName $outFileName
    }
    catch {
        $exception = $_
        Write-Host "Error getting Usage and quota report status details"
        Write-Error $exception
    }
}

function Get-AzsHubUsageData{
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Billing db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 2)]
        $numberOfRecords,
        [Switch]$exportCsv)
        
    try
    {
        if(!$numberOfRecords) {
            $numberOfRecords = 100
        }

        $dateTimeTicks = (Get-Date).Ticks
        $directoryName = 'AzsHubUsageData'
        If(!(test-path $directoryName))
        {
            New-Item -ItemType Directory -Force -Path $directoryName
        }

        if ($exportCsv)
        {
            $query = "select top $numberOfRecords * from Source.AzsHubUsageData with (nolock)
                      order by UsageStartTime desc,RecordId desc"


            Write-Host "Getting latest Azs Hub usage data..."
            $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
            $outFileName = $directoryName +'\AzsHubUsageResults_' + $dateTimeTicks + '.csv'
            Write-ResultDataToCSV -result $result -outFileName $outFileName
        }
        else
        {
            $query = "select top 1 SubscriptionId, UsageStartTime from Source.AzsHubUsageData with (nolock)
                      order by UsageStartTime desc,RecordId desc"

            $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
            Write-Host "Last available record for subscription " $result.SubscriptionId " at " $result.UsageStartTime
        }
    }
    catch {
        $exception = $_
        Write-Host "Error getting Azs Hub usage details"
        Write-Error $exception
    }
}

function Get-AzsHubUsageReaderStatus{
param(
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Billing db connection name; Please use Add-SqlDbConnection to create new connection.")]
        $connectionName,
        [Parameter(Mandatory = $false, Position = 1)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 2)]
        $numberOfRecords)

    try
    {
        if(!$numberOfRecords) {
            $numberOfRecords = 500
        }

        $dateTimeTicks = (Get-Date).Ticks
        $directoryName = 'AzsHubUsageData'
        If(!(test-path $directoryName))
        {
            New-Item -ItemType Directory -Force -Path $directoryName
        }

        $query = "select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                  where [Processor] = 'AzsHubUsageReader'
                  order by LastUpdatedTime desc"


        $getErrorStatusQuery = "select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                  where [Processor] = 'AzsHubUsageReader'
                  and ([ProcessingStatus] = 150 or [ProcessingStatus] = 160)
                  order by LastUpdatedTime desc"


        Write-Host "Getting Azs Hub usage reader status..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
        $outFileName = $directoryName +'\AzsHubUsageReaderStatusResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName

        Write-Host "Getting Azs Hub usage reader error status..."
        $result = Invoke-SqlDbQuery -name $connectionName -query $getErrorStatusQuery -dbName $dbName -printToConsole
        $outFileName = $directoryName +'\AzsHubUsageReaderErrorStatusResults_' + $dateTimeTicks + '.csv'
        Write-ResultDataToCSV -result $result -outFileName $outFileName
    }
    catch {
        $exception = $_
        Write-Host "Error getting Azs Hub usage reader status details"
        Write-Error $exception
    }
}

function Write-ResultDataToCSV($result, $outFileName)
{
    if (!$result) {
        Write-Host "No data to write"
        return
    }

    Write-Host "Writing result to File: $outFileName"
    foreach($table in $result) 
    {
        $table | Export-Csv -Path $outFileName -NoTypeInformation –Append
    }
    Write-Host "Export csv completed"
}
# SIG # Begin signature block
# MIIQQAYJKoZIhvcNAQcCoIIQMTCCEC0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUO7T1ZGul5ykfVwdF/IZmuV0Y
# +Fqgggz8MIIGcjCCBFqgAwIBAgIIZDNR08c4nwgwDQYJKoZIhvcNAQELBQAwfDEL
# MAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgw
# FgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBD
# ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwNjI0MjA0NDMwWhcNMzEw
# NjI0MjA0NDMwWjB4MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNV
# BAcMB0hvdXN0b24xETAPBgNVBAoMCFNTTCBDb3JwMTQwMgYDVQQDDCtTU0wuY29t
# IENvZGUgU2lnbmluZyBJbnRlcm1lZGlhdGUgQ0EgUlNBIFIxMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAn4MTc6qwxm0hy9uLeod00HHcjpdymuS7iDS0
# 3YADxi9FpHSavx4PUOqebXjzn/pRJqk9ndGylFc++zmJG5ErVu9ny+YL4w45jMY1
# 9Iw93SXpAawXQn1YFkDc+dUoRB2VZDBhOmTyl9dzTH17IwJt83XrVT1vqi3Er750
# rF3+arb86lx56Q9DnLVSBQ/vPrGxj9BJrabjQhlUP/MvDqHLfP4T+SM52iUcuD4A
# SjpvMjA3ZB7HrnUH2FXSGMkOiryjXPB8CqeFgcIOr4+ZXNNgJbyDWmkcJRPNcvXr
# nICb3CxnxN3JCZjVc+vEIaPlMo4+L1KYxmA3ZIyyb0pUchjMJ4f6zXWiYyFMtT1k
# /Summ1WvJkxgtLlc/qtDva3QE2ZQHwvSiab/14AG8cMRAjMzYRf3Vh+OLzto5xXx
# d1ZKKZ4D2sIrJmEyW6BW5UkpjTan9cdSolYDIC84eIC99gauQTTLlEW9m8eJGB8L
# uv+prmpAmRPd71DfAbryBNbQMd80OF5XW8g4HlbUrEim7f/5uME77cIkvkRgp3fN
# 1T2YWbRD6qpgfc3C5S/x6/XUINWXNG5dBGsFEdLTkowJJ0TtTzUxRn50GQVi7Inj
# 6iNwmOTRL9SKExhGk2XlWHPTTD0neiI/w/ijVbf55oeC7EUexW46fLFOuato95tj
# 1ZFBvKkCAwEAAaOB+zCB+DAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0E
# CQei9Xp9UlMSkpXuOIAlDaZZMDAGCCsGAQUFBwEBBCQwIjAgBggrBgEFBQcwAYYU
# aHR0cDovL29jc3BzLnNzbC5jb20wEQYDVR0gBAowCDAGBgRVHSAAMBMGA1UdJQQM
# MAoGCCsGAQUFBwMDMDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9jcmxzLnNzbC5j
# b20vc3NsLmNvbS1yc2EtUm9vdENBLmNybDAdBgNVHQ4EFgQUVML+EJUAk81q9efA
# 19myS7iPDOMwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQD1DyaH
# cK+Zosr11snwjWY9OYLTiCPYgr+PVIQnttODB9eeJ4lNhI5U0SDuYEPbV0I8x7CV
# 9r7M6qM9jk8GxitZhn/rcxvK5UAm4D1vzPa9ccbNfQ4gQDnWBdKvlAi/f8JRtyu1
# e4Mh8GPa5ZzhaS51HU7LYR71pTPfAp0V2e1pk1e6RkUugLxlvucSPt5H/5CcEK32
# VrKk1PrW/C68lyGzdoPSkfoGUNGxgCiA/tutD2ft+H3c2XBberpotbNKZheP5/Dn
# V91p/rxe4dWMnxO7lZoV+3krhdVtPmdHbhsHXPtURQ8WES4Rw7C8tW4cM1eUHv5C
# NEaOMVBO2zNXlfo45OYS26tYLkW32SLK9FpHSSwo6E+MQjxkaOnmQ6wZkanHE4Jf
# /HEKN7edUHs8XfeiUoI15LXn0wpva/6N+aTX1R1L531iCPjZ16yZSdu1hEEULvYu
# YJdTS5r+8Yh6dLqedeng2qfJzCw7e0wKeM+U9zZgtoM8ilTLTg1oKpQRdSYU6iA3
# zOt5F3ZVeHFt4kk4Mzfb5GxZxyNi5rzOLlRL/V4DKsjdHktxRNB1PjFiZYsppu0k
# 4XodhDR/pBd8tKx9PzVYy8O/Gt2fVFZtReVT84iKKzGjyj5Q0QA07CcIw2fGXOho
# v88uFmW4PGb/O7KVq5qNncyU8O14UH/sZEejnTCCBoIwggRqoAMCAQICEA0SjRWQ
# uYT7eM+eDgHqTTMwDQYJKoZIhvcNAQELBQAweDELMAkGA1UEBhMCVVMxDjAMBgNV
# BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMREwDwYDVQQKDAhTU0wgQ29ycDE0
# MDIGA1UEAwwrU1NMLmNvbSBDb2RlIFNpZ25pbmcgSW50ZXJtZWRpYXRlIENBIFJT
# QSBSMTAeFw0yMTEwMjUyMDQ1NTNaFw0yMzEwMjUyMDQ1NTNaMHcxCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdSZWRtb25kMRkwFwYD
# VQQKDBBDbG91ZCBBc3NlcnQgTExDMQswCQYDVQQLDAJVUzEZMBcGA1UEAwwQQ2xv
# dWQgQXNzZXJ0IExMQzCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAOr+
# k6LFTYXntOaV4dGI/mIyACrEE3JCr4RP5Aur5QgWuraKhL2JSh63eADxOOuk5P4E
# KXhNG8F1XW67gDLNfUlQ9ZagD3+xts/Vc8hZOqmwGw57K0/EUy5RoVVVWntMQ7DX
# q0VNp2SgMVHBuRWLvB7MX7OGTJ96+IWgEzMITBxx+bToBl+iefkJhOVZi2lCG9oN
# M3i5Yrq2T7cV1uCQwl6JNBrsaCJ64vs6pz8LzR0XmhXtg5rLYehFCqcWYCcH4Njm
# ZUVharTmBozLOTPdL6y3UReZRM5J1SxvrfRvalFQGWX4hK6OirBey1yPnhzqNHAt
# iwCLxn5l+pnTh89LLmtc1Bp8OI2nN7yaiXK13441EQFpIYnBSQJ6e8n0dDpwwoux
# OSfxtgX8iila0DBoy9vLCyGTnyXdO1zZYGoll9v8aSbvWOZu4n4gvQPVIhgROU74
# wkfGXI61Ab9ZtltF5W5WQesJoDiRIYgHUxYWU5fsTPzsoQFIXzHyaTqeJKXOtwID
# AQABo4IBhzCCAYMwHwYDVR0jBBgwFoAUVML+EJUAk81q9efA19myS7iPDOMwegYI
# KwYBBQUHAQEEbjBsMEgGCCsGAQUFBzAChjxodHRwOi8vY2VydC5zc2wuY29tL1NT
# TGNvbS1TdWJDQS1Db2RlU2lnbmluZy1SU0EtNDA5Ni1SMS5jZXIwIAYIKwYBBQUH
# MAGGFGh0dHA6Ly9vY3Nwcy5zc2wuY29tMFEGA1UdIARKMEgwCAYGZ4EMAQQBMDwG
# DCsGAQQBgqkwAQMDATAsMCoGCCsGAQUFBwIBFh5odHRwczovL3d3dy5zc2wuY29t
# L3JlcG9zaXRvcnkwEwYDVR0lBAwwCgYIKwYBBQUHAwMwTQYDVR0fBEYwRDBCoECg
# PoY8aHR0cDovL2NybHMuc3NsLmNvbS9TU0xjb20tU3ViQ0EtQ29kZVNpZ25pbmct
# UlNBLTQwOTYtUjEuY3JsMB0GA1UdDgQWBBT5QOeOXNcPYtBWMGBY9lkdLp4AczAO
# BgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAGL909UmLyhbmLPe0AyH
# mItkDXXIonmIsCrNrquwtFB5ZFhV2eQEEcFi8N+R1Pw2CGWNQe8EGN83nr1ItDNc
# JqweHvadc6i5FF1DVRPKEVHzORKKsKGZ97KyYQkT+YxJLfVCLdFCemCd2QYQuFJQ
# 4LdKcR9QZE0LvoiE9qVZ0fv2oO/4Yg/jgFTS4m1znT1IXIfCgnxfK9dr5QwQt/wX
# 3ayq554Ptbl7f6g9AGnD3U7cEaDvaPqRX16AGgxWbJU4W740UeNZnsFvdNcBHY/7
# wWxCzR03dzTGivW1aozokn05KeOyF0ZU7vhhXSeKyoaLzJXEr96r7pBUfBlVL9p9
# 6IVsHxsnPGFVZiaaZ0YQFsBWJZLEpVOIXCl2Jb2KX/NshRJGeijK0a6msVYIHKPv
# mhLnDruJkadj4RIgk8AQ2wsttUWtjWRKjD072OnAVZatRsCPIPQJsk+8gSKqfDZR
# o3DZhnrCd6TfjuoU9aULSXrwJljrOqLNOZHFoBuT7y3dZHPoo596yCmwUs+7dYCR
# nBU+hQ0Fca9aWpaYw4lKdxxhXn66EIR00TbaE3HYdHhlOc8koA9VUI/eiWdd1rKL
# j67luXYkCEJ37fE6SlyL1Jkhu3dd79+GSYlTINRnH415fH4DwiOMckj8kRbdyRV1
# tT1R5QeVMAdZHzQ80j8shEydMYICrjCCAqoCAQEwgYwweDELMAkGA1UEBhMCVVMx
# DjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMREwDwYDVQQKDAhTU0wg
# Q29ycDE0MDIGA1UEAwwrU1NMLmNvbSBDb2RlIFNpZ25pbmcgSW50ZXJtZWRpYXRl
# IENBIFJTQSBSMQIQDRKNFZC5hPt4z54OAepNMzAJBgUrDgMCGgUAoHgwGAYKKwYB
# BAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAc
# BgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUGToF
# 0TLIPpX/wQizZmBzkm9PeSYwDQYJKoZIhvcNAQEBBQAEggGAqu77WCijIakBsETB
# Kj4Wrcy5oi1ZFChRH766hQY9f+MLuC7IztDnLeNP+cVQhCLRpJLHBxhouULvqXQw
# DLKAcZ4y9yMizAn/6Hx6T+rnyDz35RhcGVp41NsNA1SMayVyuwVP52mi7xtXUSAn
# TI4/T53HGR0OQh7jH4SZoUX4Df7ok9fXkUsNzOCXDZeUApVKHR18X1xqVrp+OY4k
# u+iTHaP9oC0UI3jOabLwSsB4IW/8tcWbBYlgPEmdks3acUetdUIq/0NIYcOTZrdy
# pqkqvzMqSIw4MPja4nEJrSL+MU95zCjnJKVzot+0EZMufQOwWOXoM6T60mhzeKz5
# nzwwrio2Wjr++FGSbhbsFi+0+KqsuiRGZX8Du1k02qJQSWQp9BvqDOLW5G6aloxl
# nHbzqMux2fdTwpHdRF8uNglkEb/hOYs1TI5L3tYXmMoytYuKEBz2ZURLGTyUmJ5Q
# 564n3I2BKSM69hX5SFYEvQClIdCeho85Tm6JJL8ZBC5v60jg
# SIG # End signature block