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 Get-BillingAggregationRecordsStatus {
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, max(starttime) as MaxStartTime, 'Hourly' as TableName from BillingHourlyRecords with (nolock)
             select min(starttime) as MinStartTime, max(starttime) as MaxStartTime, 'Daily' as TableName from BillingDailyRecords with (nolock)
             select min(starttime) as MinStartTime, max(starttime) as MaxStartTime, 'Monthly' as TableName from BillingMonthlyRecords with (nolock)
             select min(starttime) as MinStartTime, max(starttime) as MaxStartTime, 'Monthly Resource Summary' as TableName from BillingMonthlyResourceSummaries with (nolock)
             select min(starttime) as MinStartTime, max(starttime) as MaxStartTime, 'Monthly Running Summary' as TableName from BillingMonthlyRunningSummary with (nolock)"

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

        if($exportCsv) 
        {
            $dateTimeTicks = (Get-Date).Ticks
            $outFileName = 'AggregationTableStatusResults_' + $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 Aggregation table status 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 = $true, Position = 3)]
        $partitionKey1,
        [Parameter(Mandatory = $false, Position = 4)]
        $dbName,
        [Parameter(Mandatory = $false, Position = 5)]
        $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
 
        DROP TABLE IF EXISTS #invoiceIds
 
        SELECT Id into #invoiceIds from [dbo].[BillingInvoices] WHERE InvoiceStartTime = @lastProcessedTime
        DELETE FROM [dbo].[BillingInvoiceSendInfos]
        DELETE FROM [dbo].[BillingInvoiceLineItems] WHERE InvoiceId IN (SELECT Id FROM [dbo].BillingInvoices WHERE InvoiceStartTime >= @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
        DELETE FROM [dbo].[CustomReports] WHERE BillingCycle >= @lastProcessedTime
 
        UPDATE [dbo].BillingMonthlyResourceSummaries set InvoiceId=null, IsProcessed=1,IsError=1, ErrorText = NULL where InvoiceId in (select id from #invoiceIds)
 
        ---------CSP License-------
        UPDATE [dbo].[UsageProcessingStates]
            SET [LastProcessedTime] = @lastprocessedtime
            WHERE StateName = 'CspLicenseLogProcessor'
 
        UPDATE [dbo].[ReportDefinition]
            SET [LastProcessedTime] = @lastprocessedtime
 
        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 below 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 (PartitionKey1 is null or PartitionKey1 = '$partitionKey1' ) and 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 (PartitionKey1 is null or PartitionKey1 = '$partitionKey1' ) and RecordId >= @lastAzsHubCostRecordId
 
        DELETE from [dbo].[CspCostRecordProcessingStatus] WHERE (PartitionKey1 is null or PartitionKey1 = '$partitionKey1' ) and RecordId IN(
           SELECT RecordId FROM [$costManagementDBName].Source.CspAzureInvoiceLineItems Where InvoiceId IN(
              SELECT InvoiceId FROM [$costManagementDBName].Source.CspInvoices Where BillingPeriodStartDate >= @lastProcessedTime))
 
        DECLARE @lastCspLicenseCostRecordId INT
        SELECT TOP 1 @lastCspLicenseCostRecordId = RecordId FROM [$costManagementDBName].Source.CspLicenseInvoiceLineItems
        WHERE ChargeStartDate >= @lastProcessedTime ORDER BY RecordId
        DELETE from [dbo].CspLicenseCostRecordProcessingStatus WHERE (PartitionKey1 is null or PartitionKey1 = '$partitionKey1' ) and RecordId >= @lastCspLicenseCostRecordId
 
        DECLARE @lastCspEstimateCostRecordId INT
        SELECT TOP 1 @lastCspEstimateCostRecordId = RecordId FROM [$costManagementDBName].[Source].[CspAzureUnBilledLineItems]
        WHERE ChargeStartDate >= @lastProcessedTime ORDER BY RecordId
        DELETE from [dbo].CspCostEstimateRecordProcessingStatus WHERE (PartitionKey1 is null or PartitionKey1 = '$partitionKey1' ) and 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,
        [Switch]$exportCsv)
        
    try
    {
        if($exportCsv)
        {
            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] = 'AzsHubReportProcessor'
                         order by [ProcessingStartTime] desc
                         select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                         where [Processor] = 'AzsHubReportCsrlLookupProcessor'
                         order by [ProcessingStartTime] desc
                         select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                         where [Processor] = 'AzsHubSubscriptionQuotaStatusProcessor'
                         order by [ProcessingStartTime] desc
                         select top $numberOfRecords * from Pipeline.ProcessorStatus with (nolock)
                         where [Processor] = 'AzsHubDailyReportProcessor'
                         order by [ProcessingStartTime] 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'
            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
        }

        Write-Host "Getting latest report status data..."
        $reportProcessorStatus = "select top 1 State,ProcessingStatus,ProcessingStartTime,Message From Pipeline.ProcessorStatus with (nolock)
                                  where [Processor] = 'AzsHubReportProcessor'
                                  order by [ProcessingStartTime] desc"

        $result = Invoke-SqlDbQuery -name $connectionName -query $reportProcessorStatus -dbName $dbName -printToConsole
        $result
    }
    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 Get-SubscriptionMapper{
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)]
        $SubscriptionId,
        [Switch]$exportCsv)
        
    try
    {
        $dateTimeTicks = (Get-Date).Ticks
        $directoryName = 'SubscriptionMapper'
        If(!(test-path $directoryName))
        {
            New-Item -ItemType Directory -Force -Path $directoryName
        }
        if ($SubscriptionId)
        {
            $query = "select * from SubscriptionMapper with (nolock) where RegisteredSubscriptionId = '$SubscriptionId'"
        }
        else{
            $query = "select * from SubscriptionMapper with (nolock)"
        }

        if ($exportCsv)
        {
            Write-Host "Getting Subscription Mapper data..."
            $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
            $outFileName = $directoryName +'\SubscriptionMapperResults_' + $dateTimeTicks + '.csv'
            Write-ResultDataToCSV -result $result -outFileName $outFileName
        }
        else
        {
            $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
            Write-Host "Getting Subscription Mapper data..."
            Write-Output $result | fl *
        }
    }
    catch {
        $exception = $_
        Write-Host "Error getting Subscription Mapper data"
        Write-Error $exception
    }
}

function Clean-EmailDispatchQueue{
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]$isUpdateStatus)
    try
    {
        #deleting/updating the email dispatch queue entries if the status is New/Error
        if (!$isUpdateStatus)
        {
            $query = "DELETE FROM EmailDispatchQueue WHERE [Status] != 20"
        }
        else
        {
            $query = "UPDATE EmailDispatchQueue SET [Status] = 0, [StatusString] = 'None',
                                                    [DispatchErrorMessage] = 'Updated from powershell command Clean-EmailDispatchQueue'
                                                WHERE [Status] != 20"

        }
        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dbName -printToConsole
        Write-Host "Successfully cleared Email Dispatch Queue"
    }
    catch {
        $exception = $_
        Write-Host "Error cleaning Email Dispatch Queue"
        Write-Error $exception
    }
}

function Get-BillingUsageSources{
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)]
        $billingDBName,
        [Parameter(Mandatory = $false, Position = 2)]
        $platformType,
        [Parameter(Mandatory = $false, Position = 3)]
        $sourceType,
        [Switch]$exportCsv)
    try
    {
        $query = "SELECT * from UsageSource with (nolock)"
        if ($platformType -and $sourceType)
        {
            $query = $query + " WHERE PlatformType = '$platformType' AND SourceType = '$sourceType'"
        }
        elseif ($platformType)
        {
            $query = $query + " WHERE PlatformType = '$platformType'"
        }
        elseif ($sourceType)
        {
            $query = $query + " WHERE SourceType = '$sourceType'"
        }
        $billingQueryResult = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $billingDBName -printToConsole
        Write-Host "Usage Source count: "$billingQueryResult.Count
        $billingQueryResult
        if ($exportCsv)
        {
            $dateTimeTicks = (Get-Date).Ticks
            $outFileName = '.\BillingUsageSourceResults_' + $dateTimeTicks + '.csv'
            Write-ResultDataToCSV -result $billingQueryResult -outFileName $outFileName
        }
    }
    catch {
        $exception = $_
        Write-Host "Error getting Usage sources"
        Write-Error $exception
    }
}

function Get-DACMConnections{
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)]
        $dacmDBName,
        [Parameter(Mandatory = $false, Position = 2)]
        $connectionType)
    try
    {
        $query = "SELECT Id,Name,ConnectionType,SourceName,SourceRecordId,IsDeleted,CreatedTime,LastUpdatedTime from Config.Connections with (nolock)"
        if ($connectionType)
        {
            $query = $query + " WHERE ConnectionType = '$connectionType'"
        }
        $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dacmDBName -printToConsole
        Write-Host "Azs connections available in DACM: "$result.Count
        $result
    }
    catch {
        $exception = $_
        Write-Host "Error getting Azs DACM connections"
        Write-Error $exception
    }
}

function Delete-AzsBillingConnection{
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)]
        $billingDBName,
        [Parameter(Mandatory = $true, Position = 2)]
        $dacmDBName,
        [Parameter(Mandatory = $false, Position = 3)]
        $azsDACMConnectionName,
        [Parameter(Mandatory = $false, Position = 4)]
        $azsDACMConnectionId)
    try
    {
        #deleting the connection using dacm connection name or id
        if ($azsDACMConnectionName)
        {
            $query = "UPDATE Config.Connections SET [IsDeleted] = 1, LastUpdatedTime = GETUTCDATE() WHERE [Name] = '$azsDACMConnectionName'"
            $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dacmDBName -printToConsole
            Write-Host "Successfully cleared Azs connection in DACM"
            $query = "SELECT Id FROM Config.Connections with (nolock) WHERE [Name] = '$azsDACMConnectionName'"
            $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dacmDBName -printToConsole
            $azsDACMConnectionId = $result.Id
        }
        else
        {
            $query = "UPDATE Config.Connections SET [IsDeleted] = 1, LastUpdatedTime = GETUTCDATE() WHERE [Id] = $azsDACMConnectionId"
            $result = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $dacmDBName -printToConsole
            Write-Host "Successfully cleared Azs connection in DACM"
        }
        $query = "UPDATE UsageSource SET [IsDeleted] = 1, UpdatedTime = GETUTCDATE() WHERE [SourceConnectionId] = $azsDACMConnectionId AND [PlatformType] = 'AzsHub' AND [SourceType] = 'Dacm'"
        $billingQueryResult = Invoke-SqlDbQuery -name $connectionName -query $query -dbName $billingDBName -printToConsole
        Write-Host "Successfully cleared Azs connection in Billing"
    }
    catch {
        $exception = $_
        Write-Host "Error cleaning Azs connection in DACM and Billing"
        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
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUKVW2seKrDwcJzXvrVsaeTUTg
# wnCgggz8MIIGcjCCBFqgAwIBAgIIZDNR08c4nwgwDQYJKoZIhvcNAQELBQAwfDEL
# 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
# BgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUoi9H
# 4hZTERPOJ98ULWqZ75+UZ2kwDQYJKoZIhvcNAQEBBQAEggGAgk1M6PVn8+Lrvg6P
# ghOHI7MkOGqWXPSJ8229NMyWIZ4eY8n9VTzFhbx2R8Pc80pAtZAlLpK9ngmYAT7q
# ExN4A52O8wmEF3C6Mk7mtwmeIUT5dnw83MrgX2LOQoz3HXZvB8qVf8YoUHcHw48h
# iqw806aIlX1bE45Vh7SBmh89G3HEdbKDxsibUN/NY91R1RKcmWILYl+/Nm7bXq1b
# E6ZwNrPk6YAzv2bY7K9wsoPQ+5Gg6KZllcrrT4z8uYpJ3XtU5EpvoIRQiOHheSrz
# UAa2cKzk2QIgd8O1FpWY+ueoViEIKDmZ+MBx4Y5p/1BqqMsUXON3QV6MCHTIWFlE
# tMLNzJxFtYbbCbG3yZIENfoQf6eC1vcHYX103UOnA0ErxSO0JbCISTUdjw/VeB1l
# SSQCq08wURO4kJ8rI4eEWoZKW1FsFMDwmbzUfYf+xkBmLmK4/HKTuz+6IFkRbtF1
# Ilw3Wqljir0nCf/eEW+Z6qSc8cpR0dvPYNmOVwz77ERs0YYY
# SIG # End signature block