PSWinDocumentation.O365HealthService.psm1

function Connect-O365ServiceHealth {
    param([string][alias('ClientID')] $ApplicationID,
        [string][alias('ClientSecret')] $ApplicationKey,
        [string] $TenantDomain)
    $Body = @{grant_type = "client_credentials"
        resource = "https://manage.office.com"
        client_id = $ApplicationID
        client_secret = $ApplicationKey
    }
    try {$Authorization = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$($TenantDomain)/oauth2/token?api-version=1.0" -Body $body -ErrorAction Stop} catch {
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        Write-Warning -Message "Connect-O365ServiceHealth - Error: $ErrorMessage"
    }
    if ($Authorization) {@{'Authorization' = "$($Authorization.token_type) $($Authorization.access_token)"}
    } else {$null}
}
function ConvertFrom-UTCTime {
    [CmdLetbinding()]
    param([Object] $Time,
        [switch] $ToLocalTime)
    if ($null -eq $Script:TimeZoneBias) {$TimeZoneBias = (Get-CimInstance -ClassName Win32_TimeZone).Bias} else {$TimeZoneBias = $Script:TimeZoneBias}
    if ($Time -is [DateTime]) {$ConvertedTime = $Time} else {
        if ($null -eq $Time -or $Time -eq '') {return} else {
            $NewTime = $Time -replace ', at', '' -replace 'UTC', '' -replace 'at' -replace '(^.+?,)'
            try {[DateTime] $ConvertedTime = [DateTime]::Parse($NewTime)} catch {
                Write-Warning "ConvertFrom-UTCTime - couldn't convert time. Please report on GitHub - $Time. Skipping conversion..."
                return $Time
            }
        }
    }
    if ($ToLocal) {$ConvertedTime.AddMinutes($TimeZoneBias)} else {$ConvertedTime}
}
function Get-Office365Health {
    [CmdLetbinding()]
    param([string][alias('ClientID')] $ApplicationID,
        [string][alias('ClientSecret')] $ApplicationKey,
        [string] $TenantDomain,
        [PSWinDocumentation.Office365Health[]] $TypesRequired = [PSWinDocumentation.Office365Health]::All,
        [switch] $ToLocalTime)
    $StartTime = Start-TimeLog
    $Script:TimeZoneBias = (Get-CimInstance -ClassName Win32_TimeZone).Bias
    $Script:Today = Get-Date
    if ($null -eq $TypesRequired -or $TypesRequired -contains [PSWinDocumentation.Office365Health]::All) {$TypesRequired = Get-Types -Types ([PSWinDocumentation.Office365Health])}
    $Authorization = Connect-O365ServiceHealth -ApplicationID $ApplicationID -ApplicationKey $ApplicationKey -TenantDomain $TenantDomain
    if ($null -ne $Authorization) {
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::Services,
                [PSWinDocumentation.Office365Health]::ServicesExtended)) {$Services = Get-Office365ServiceHealthServices -Authorization $Authorization -TenantDomain $TenantDomain}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::CurrentStatus,
                [PSWinDocumentation.Office365Health]::CurrentStatusExtended)) {$CurrentStatus = Get-Office365ServiceHealthCurrentStatus -Authorization $Authorization -TenantDomain $TenantDomain -ToLocalTime:$ToLocalTime}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::HistoricalStatus,
                [PSWinDocumentation.Office365Health]::HistoricalStatusExtended)) {$HistoricalStatus = Get-Office365ServiceHealthHistoricalStatus -Authorization $Authorization -TenantDomain $TenantDomain -ToLocalTime:$ToLocalTime}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::Incidents,
                [PSWinDocumentation.Office365Health]::IncidentsExtended,
                [PSWinDocumentation.Office365Health]::MessageCenterInformation,
                [PSWinDocumentation.Office365Health]::MessageCenterInformationExtended,
                [PSWinDocumentation.Office365Health]::PlannedMaintenance,
                [PSWinDocumentation.Office365Health]::PlannedMaintenanceExtended,
                [PSWinDocumentation.Office365Health]::Messages)) {$Messages = Get-Office365ServiceHealthMessages -Authorization $Authorization -TenantDomain $TenantDomain -ToLocalTime:$ToLocalTime}
        $Output = [ordered] @{}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::Services)) {$Output.Services = $Services.Simple}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::ServicesExtended)) {$Output.ServicesExtended = $Services.Extended}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::CurrentStatus)) {$Output.CurrentStatus = $CurrentStatus.Simple}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::CurrentStatusExtended)) {$Output.CurrentStatusExtended = $CurrentStatus.Extended}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::HistoricalStatus)) {$Output.HistoricalStatus = $HistoricalStatus.Simple}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::HistoricalStatusExtended)) {$Output.HistoricalStatusExtended = $HistoricalStatus.Extended}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::MessageCenterInformation)) {$Output.MessageCenterInformation = $Messages.MessageCenterInformationSimple | Sort-Object -Property LastUpdatedTime -Descending}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::MessageCenterInformationExtended)) {$Output.MessageCenterInformationExtended = $Messages.MessageCenterInformation | Sort-Object -Property LastUpdatedTime -Descending}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::Incidents)) {$Output.Incidents = $Messages.IncidentsSimple | Sort-Object -Property LastUpdatedTime -Descending}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::Messages)) {$Output.IncidentsMessages = $Messages.Messages | Sort-Object -Property PublishedTime -Descending}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::IncidentsExtended)) {$Output.IncidentsExtended = $Messages.Incidents | Sort-Object -Property LastUpdatedTime -Descending}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::PlannedMaintenance)) {$Output.PlannedMaintenance = $Messages.PlannedMaintenanceSimple | Sort-Object -Property LastUpdatedTime -Descending}
        if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.Office365Health]::PlannedMaintenanceExtended)) {$Output.PlannedMaintenanceExtended = $Messages.PlannedMaintenance | Sort-Object -Property LastUpdatedTime -Descending}
        $EndTime = Stop-TimeLog -Time $StartTime -Option OneLiner
        Write-Verbose "Get-Office365Health - Time to process: $EndTime"
        return $Output
    } else {return}
}
function Get-Office365ServiceHealthCurrentStatus {
    param([System.Collections.IDictionary] $Authorization,
        [string] $TenantDomain,
        [switch] $ToLocalTime)
    $CurrentStatus = (Invoke-RestMethod -Uri "https://manage.office.com/api/v1.0/$($TenantDomain)/ServiceComms/CurrentStatus" -Headers $Authorization -Method Get)
    $Output = @{}
    $Output.Simple = foreach ($Status in $CurrentStatus.Value) {
        [PSCustomObject][ordered] @{Service = $Status.WorkloadDisplayName
            ServiceStatus = $Status.StatusDisplayName
            StatusTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $Status.StatusTime
            IncidentIds = $Status.IncidentIds -join ', '
        }
    }
    $Output.Extended = foreach ($Status in $CurrentStatus.Value) {
        foreach ($Feature in $Status.FeatureStatus) {
            [PSCustomObject][ordered] @{Service = $Status.WorkloadDisplayName
                ServiceStatus = $Status.StatusDisplayName
                Feature = $Feature.FeatureDisplayName
                FeatureStatus = $Feature.FeatureServiceStatusDisplayName
                IncidentIds = $Status.IncidentIds -join ', '
                StatusTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $Status.StatusTime
            }
        }
    }
    return $Output
}
function Get-Office365ServiceHealthHistoricalStatus {
    [CmdLetbinding()]
    param([System.Collections.IDictionary] $Authorization,
        [string] $TenantDomain,
        [switch] $ToLocalTime)
    $HistoricalStatus = (Invoke-RestMethod -Uri "https://manage.office.com/api/v1.0/$($TenantDomain)/ServiceComms/HistoricalStatus" -Headers $Authorization -Method Get)
    $Output = @{}
    $Output.Simple = foreach ($Status in $HistoricalStatus.Value) {
        $StatusTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $Status.StatusTime
        [PSCustomObject][ordered] @{Service = $Status.WorkloadDisplayName
            ServiceStatus = $Status.StatusDisplayName
            IncidentIds = $Status.IncidentIds -join ', '
            StatusTime = $StatusTime
            StatusDaysAgo = Convert-TimeToDays -StartTime $StatusTime -EndTime $Script:Today
        }
    }
    $Output.Extended = foreach ($Status in $HistoricalStatus.Value) {
        foreach ($Feature in $Status.FeatureStatus) {
            $StatusTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $Status.StatusTime
            [PSCustomObject][ordered] @{Service = $Status.WorkloadDisplayName
                ServiceStatus = $Status.StatusDisplayName
                Feature = $Feature.FeatureDisplayName
                FeatureStatus = $Feature.FeatureServiceStatusDisplayName
                IncidentIds = $Status.IncidentIds -join ', '
                StatusTime = $StatusTime
                StatusDaysAgo = Convert-TimeToDays -StartTime $StatusTime -EndTime $Script:Today
            }
        }
    }
    return $Output
}
function Get-Office365ServiceHealthMessages {
    param([System.Collections.IDictionary] $Authorization,
        [string] $TenantDomain,
        [switch] $ToLocalTime)
    $AllMessages = (Invoke-RestMethod -Uri "https://manage.office.com/api/v1.0/$($TenantDomain)/ServiceComms/Messages" -Headers $Authorization -Method Get)
    $Output = @{}
    $Simple = foreach ($Message in $AllMessages.Value) {
        $LastUpdatedTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $Message.LastUpdatedTime
        [PSCustomObject][ordered] @{Id = $Message.Id
            Title = $Message.Title
            ImpactDescription = $Message.ImpactDescription
            LastUpdatedTime = $LastUpdatedTime
            LastUpdatedDaysAgo = Convert-TimeToDays -StartTime $LastUpdatedTime -EndTime $Script:Today
            MessageType = $Message.MessageType
            Status = $Message.Status
            Severity = $Message.Severity
            StartTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $Message.StartTime
            Workload = $Message.WorkloadDisplayName
            ActionType = $Message.ActionType
            Classification = $Message.Classification
            EndTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $Message.EndTime
            Feature = $Message.FeatureDisplayName
            UserFunctionalImpact = $Message.UserFunctionalImpact
            PostIncidentDocumentUrl = $Message.PostIncidentDocumentUrl
            AffectedTenantCount = $Message.AffectedTenantCount
            AffectedUserCount = $Message.AffectedUserCount
            AffectedWorkload = $Message.AffectedWorkloadDisplayNames -join ','
        }
    }
    $Extended = foreach ($Message in $AllMessages.Value) {
        $Messages = $Message.Messages
        foreach ($M in $Messages) {
            $LastUpdatedTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $Message.LastUpdatedTime
            $PublishedTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $M.PublishedTime
            [PSCustomObject][ordered] @{Id = $Message.Id
                Title = $Message.Title
                ImpactDescription = $Message.ImpactDescription
                LastUpdatedTime = $LastUpdatedTime
                LastUpdatedDaysAgo = Convert-TimeToDays -StartTime $LastUpdatedTime -EndTime $Script:Today
                MessageType = $Message.MessageType
                Status = $Message.Status
                Severity = $Message.Severity
                StartTime = ConvertFrom-UTCTime -Time $Message.StartTime -ToLocalTime:$ToLocalTime
                Message = $M.MessageText
                PublishedTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $M.PublishedTime
                PublishedDaysAgo = Convert-TimeToDays -StartTime $PublishedTime -EndTime $Script:Today
                Workload = $Message.WorkloadDisplayName
                ActionType = $Message.ActionType
                Classification = $Message.Classification
                EndTime = ConvertFrom-UTCTime -ToLocalTime:$ToLocalTime -Time $Message.EndTime
                Feature = $Message.FeatureDisplayName
                UserFunctionalImpact = $Message.UserFunctionalImpact
                PostIncidentDocumentUrl = $Message.PostIncidentDocumentUrl
                AffectedTenantCount = $Message.AffectedTenantCount
                AffectedUserCount = $Message.AffectedUserCount
                AffectedWorkload = $Message.AffectedWorkloadDisplayNames -join ','
            }
        }
    }
    $MessageCenterInformationSimple = foreach ($_ in $Simple) {if ($_.MessageType -eq 'MessageCenter') {$_}}
    $Output.MessageCenterInformationSimple = foreach ($_ in $MessageCenterInformationSimple) {
        [PSCustomObject][ordered] @{ID = $_.ID
            Title = $_.Title
            LastUpdatedTime = $_.LastUpdatedTime
            LastUpdatedDaysAgo = $_.LastUpdatedDaysAgo
            Severity = $_.Severity
            StartTime = $_.StartTime
            EndTime = $_.EndTime
            ActionType = $_.ActionType
            Classification = $_.Classification
            AffectedService = $_.AffectedWorkload
            MessageType = $_.MessageType
        }
    }
    $MessageCenterInformation = foreach ($_ in $Extended) {if ($_.MessageType -eq 'MessageCenter') {$_}}
    $Output.MessageCenterInformation = foreach ($_ in $MessageCenterInformation) {
        [PSCustomObject][Ordered] @{ID = $_.ID
            PublishedTime = $_.PublishedTime
            PublishedDaysAgo = $_.PublishedDaysAgo
            Title = $_.Title
            Message = $_.Message
            LastUpdatedTime = $_.LastUpdatedTime
            LastUpdatedDaysAgo = $_.LastUpdatedDaysAgo
            Severity = $_.Severity
            StartTime = $_.StartTime
            EndTime = $_.EndTime
            ActionType = $_.ActionType
            Classification = $_.Classification
            AffectedService = $_.AffectedWorkload
            MessageType = $_.MessageType
        }
    }
    $IncidentsSimple = foreach ($_ in $Simple) {if ($_.MessageType -eq 'Incident') {$_}}
    $Output.IncidentsSimple = foreach ($_ in $IncidentsSimple) {
        [PSCustomObject][ordered] @{Service = $_.Workload
            Feature = $_.Feature
            ID = $_.ID
            Title = $_.Title
            ImpactDescription = $_.ImpactDescription
            LastUpdatedTime = $_.LastUpdatedTime
            LastUpdatedDaysAgo = $_.LastUpdatedDaysAgo
            UserFunctionalImpact = $_.UserFunctionalImpact
            PostIncidentDocumentUrl = $_.PostIncidentDocumentUrl
            Severity = $_.Severity
            StartTime = $_.StartTime
            EndTime = $_.EndTime
            Classification = $_.Classification
            AffectedTenantCount = $_.AffectedTenantCount
            AffectedUserCount = $_.AffectedUserCount
            MessageType = $_.MessageType
        }
    }
    $Incidents = foreach ($_ in $Extended) {if ($_.MessageType -eq 'Incident') {$_}}
    $Output.Incidents = foreach ($_ in $Incidents) {
        [PSCustomObject][Ordered] @{Service = $_.Workload
            Feature = $_.Feature
            ID = $_.ID
            Title = $_.Title
            ImpactDescription = $_.ImpactDescription
            PublishedTime = $_.PublishedTime
            PublishedDaysAgo = $_.PublishedDaysAgo
            Message = $_.Message
            LastUpdatedTime = $_.LastUpdatedTime
            LastUpdatedDaysAgo = $_.LastUpdatedDaysAgo
            UserFunctionalImpact = $_.UserFunctionalImpact
            PostIncidentDocumentUrl = $_.PostIncidentDocumentUrl
            Severity = $_.Severity
            StartTime = $_.StartTime
            EndTime = $_.EndTime
            Classification = $_.Classification
            AffectedTenantCount = $_.AffectedTenantCount
            AffectedUserCount = $_.AffectedUserCount
            MessageType = $_.MessageType
        }
    }
    $Output.Messages = foreach ($Entry in $Extended) {
        $LimitedEntry = foreach ($_ in $Entry) {if ($_.MessageType -eq 'Incident') {$_}}
        foreach ($_ in $LimitedEntry) {
            $Object = [PsCustomObject][Ordered] @{Service = $_.Workload
                Status = $_.Status
                PublishedTime = $_.PublishedTime
                PublishedDaysAgo = $_.PublishedDaysAgo
                Title = ''
                UserImpact = ''
                MoreInfo = ''
                CurrentStatus = ''
                ScopeOfImpact = ''
                StartTime = ''
                PreliminaryRootCause = ''
                NextUpdateBy = ''
                FinalStatus = ''
                Other = ''
            }
            foreach ($SubMessage in $_.Message.Split([Environment]::NewLine)) {
                if ($SubMessage -like 'Title: *') {$Object.Title = $SubMessage -replace 'Title: ', ''} elseif ($SubMessage -like 'User Impact: *') {$Object.UserImpact = $SubMessage -replace 'User Impact: ', ''} elseif ($SubMessage -like 'More info: *') {$Object.MoreInfo = $SubMessage -replace 'More info: ', ''} elseif ($SubMessage -like 'Current status: *') {$Object.CurrentStatus = $SubMessage -replace 'Current status: ', ''} elseif ($SubMessage -like 'Scope of impact: *') {$Object.ScopeOfImpact = $SubMessage -replace 'Scope of impact: ', ''} elseif ($SubMessage -like 'Start time: *') {
                    $Time = $SubMessage -replace 'Start time: ', ''
                    $Object.StartTime = ConvertFrom-UTCTime -Time $Time -ToLocalTime:$ToLocalTime
                } elseif ($SubMessage -like 'Preliminary root cause: *') {$Object.PreliminaryRootCause = $SubMessage -replace 'Preliminary root cause: ', ''} elseif ($SubMessage -like 'Next update by: *') {
                    $Time = ($SubMessage -replace 'Next update by: ', '').Trim()
                    $Object.NextUpdateBy = ConvertFrom-UTCTime -Time $Time -ToLocalTime:$ToLocalTime
                } elseif ($SubMessage -like 'Final status: *') {$Object.FinalStatus = ($SubMessage -replace 'Final status: ', '').Trim()} else {$Object.Other = $SubMessage.Trim()}
            }
            $Object
        }
    }
    $Output.PlannedMaintenanceSimple = foreach ($_ in $Simple) {if ($_.MessageType -eq 'PlannedMaintenance') {$_}}
    $Output.PlannedMaintenance = foreach ($_ in $Extended) {if ($_.MessageType -eq 'PlannedMaintenance') {$_}}
    return $Output
}
function Get-Office365ServiceHealthServices {
    param([System.Collections.IDictionary] $Authorization,
        [string] $TenantDomain)
    $Services = (Invoke-RestMethod -Uri "https://manage.office.com/api/v1.0/$($TenantDomain)/ServiceComms/Services" -Headers $Authorization -Method Get)
    $Output = @{}
    $Output.Simple = foreach ($Service in $Services.Value) {[PSCustomObject][ordered] @{Service = $Service.DisplayName}
    }
    $Output.Extended = foreach ($Service in $Services.Value) {
        foreach ($Feature in $Service.Features) {
            [PSCustomObject][ordered] @{Service = $Service.DisplayName
                Feature = $Feature.DisplayName
            }
        }
    }
    return $Output
}
Add-Type -TypeDefinition @"
    using System;
 
    namespace PSWinDocumentation
    {
        [Flags]
        public enum Office365Health {
            All,
            Services,
            ServicesExtended,
            CurrentStatus,
            CurrentStatusExtended,
            HistoricalStatus,
            HistoricalStatusExtended,
            MessageCenterInformation,
            MessageCenterInformationExtended,
            Incidents,
            IncidentsExtended,
            PlannedMaintenance,
            PlannedMaintenanceExtended,
            Messages
        }
    }
"@

Export-ModuleMember -Function @('Get-Office365Health') -Alias @()