StatuspagePS.psm1

function Invoke-StatuspageRestMethod
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory)]
        [string] $page,

        [hashtable] $body,

        [ValidateSet('Get', 'Post', 'Patch', 'Delete')]
        [string] $method = 'Get',

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )

    if ($script:statuspageUrl -eq $null)
    {
        throw 'Unknown Statuspage url (run Set-StatuspageUrl)'
    }
    if ($script:statuspageHeaders -eq $null)
    {
        throw 'Unknown Statuspage headers (run Set-StatuspageHeaders)'
    }
    
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::TLS12
    $retryAttempt = 0
    $jsonBody = $body | ConvertTo-Json
    $uri = "$script:statuspageUrl/$page"
    do
    {
        try
        {
            (Invoke-RestMethod -Uri $uri -Headers $script:statuspageHeaders -Body $jsonBody -Method $method -ErrorAction Stop)
            $done = $true
        }
        catch
        {
            $errorMessage = $_.ErrorDetails.Message | ConvertFrom-Json | Select-Object -ExpandProperty error
            Write-Verbose "$($MyInvocation.MyCommand): retryAttempt=$retryAttempt/$retryCount errorMessage='$errorMessage' uri=$uri method=$method body=$jsonBody" -Verbose
            if ($retryAttempt -lt $retryCount)
            {
                Start-Sleep -Seconds $retrySecDelay
                $retryAttempt++
            }
            else
            {
                throw $_
            }
        }
    }
    while (-not $done)
}

function Convert-StatuspageComponent
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [object] $object
    )

    begin
    {
        $components = @()
    }
    process
    {
        $components += [PSCustomObject] @{
            PSTypeName = 'statuspageComponent'
            id = $object.id
            pageId = $object.page_id
            groupId = $object.group_id
            createdAt = $object.created_at
            updatedAt = $object.updated_at
            group = $object.group
            name = $object.name
            description = $object.description
            position = $object.position
            status = $object.status
            showcase = $object.showcase
            onlyShowIfDegraded = $object.only_show_if_degraded
            automationEmail = $object.automation_email
        }
    }
    end
    {
        $components
    }
}

function Convert-StatuspageIncident
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [object] $object
    )

    begin
    {
        $incidents = @()
    }
    process
    {
        $incidents += [PSCustomObject] @{
            PSTypeName = 'statuspageIncident'
            id = $object.id
            name = $object.name
            status = $object.status
            createdAt = $object.created_at
            updatedAt = $object.updated_at
            monitoringAt = $object.monitoring_at
            resolvedAt = $object.resolved_at
            impact = $object.impact
            shortlink = $object.shortlink
            scheduledFor = $object.scheduled_for
            scheduledUntil = $object.scheduled_until
            scheduledRemindPrior = $object.scheduled_remind_prior
            scheduledRemindedAt = $object.scheduled_reminded_at
            impactOverride = $object.impact_override
            scheduledAutoInProgress = $object.scheduled_auto_in_progress
            scheduledAutoCompleted = $object.scheduled_auto_completed
            metadata = $object.metadata
            startedAt = $object.started_at
            pageId = $object.page_id
            incidentUpdates = $object.incident_updates
            postmortemBody = $object.postmortem_body
            postmortemBodyLastUpdatedAt = $object.postmortem_body_last_updated_at
            postmortemIgnored = $object.postmortem_ignored
            postmortemPublishedAt = $object.postmortem_published_at
            postmortemNotifiedSubscribers = $object.postmortem_notified_subscribers
            postmortemNotifiedTwitter = $object.postmortem_notified_twitter
            components = ($object.components | Convert-StatuspageComponent)
        }
    }
    end
    {
        $incidents
    }
}

function Set-StatuspageUrl
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [string] $pageId
    )

    $script:statuspageUrl = "https://api.statuspage.io/v1/pages/$pageId"
}

function Set-StatuspageHeaders
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [string] $apiKey
    )

    $script:statuspageHeaders = @{
        'Authorization' = "OAuth $apiKey"
        'Content-Type' = 'application/json'
    }
}

function Get-StatuspageComponent
{
    [CmdletBinding(DefaultParameterSetName = 'NoneParameterSetName')]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByName')]
        [string] $name,

        [Parameter(Mandatory, ParameterSetName = 'ById')]
        [string] $id,

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )
    
    begin
    {
        $components = Invoke-StatuspageRestMethod -page components -Method Get -retryCount $retryCount -retrySecDelay $retrySecDelay | Convert-StatuspageComponent
    }
    process
    {
        if ($name)
        {
            $component = $components | Where-Object { $_.name -eq $name }
            if ($component)
            {
                $component
            }
            else
            {
                throw "Cannot find component with name $name"
            }
        }
        elseif ($id)
        {
            $component = $components | Where-Object { $_.id -eq $id }
            if ($component)
            {
                $component
            }
            else
            {
                throw "Cannot find component with id $id"
            }
        }
        else
        {
            $components
        }
    }
}

function New-StatuspageComponent
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory, ValueFromPipeline)]
        [string] $name,

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )

    process
    {
        $body = @{
            component = @{
                name = $name
            }
        }

        Invoke-StatuspageRestMethod -page components -Method Post -retryCount $retryCount -retrySecDelay $retrySecDelay -Body $body | Convert-StatuspageComponent
    }
}

function New-StatuspageComponentGroup
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [parameter(Mandatory, ParameterSetName = 'ById')]
        [string] $name,

        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [PSTypeName('statuspageComponent')] $component,

        [parameter(Mandatory, ParameterSetName = 'ById')]
        [string[]] $componentId,

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )

    if ($PsCmdlet.ParameterSetName -eq 'ByObject')
    {
        $thisComponentId = $component.id
    }
    elseif ($PsCmdlet.ParameterSetName -eq 'ById')
    {
        $thisComponentId = $componentId
    }
    
    $body = @{
        component_group = @{
            name = $name
            components = $thisComponentId
        }
    }

    Invoke-StatuspageRestMethod -page component-groups -Method Post -retryCount $retryCount -retrySecDelay $retrySecDelay -Body $body | Convert-StatuspageComponent
}

function Update-StatuspageComponent
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByObject')]
        [PSTypeName('statuspageComponent')] $component,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ById')]
        [string[]] $componentId,

        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [parameter(Mandatory, ParameterSetName = 'ById')]
        [ValidateSet('operational', 'major_outage', 'degraded_performance', 'partial_outage', 'under_maintenance')]
        [string] $status,

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )

    begin
    {
        $body = @{
            component = @{
                status = $status
            }
        }
    }
    process
    {
        if ($PsCmdlet.ParameterSetName -eq 'ByObject')
        {
            $thisComponentId = $component.id
        }
        elseif ($PsCmdlet.ParameterSetName -eq 'ById')
        {
            $thisComponentId = $componentId
        }

        Invoke-StatuspageRestMethod -page "components/$thisComponentId" -method Patch -retryCount $retryCount -retrySecDelay $retrySecDelay -body $body | Convert-StatuspageComponent
    }
}

function Remove-StatuspageComponent
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByObject')]
        [PSTypeName('statuspageComponent')] $component,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ById')]
        [string] $componentId,

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )

    process
    {
        if ($PsCmdlet.ParameterSetName -eq 'ByObject')
        {
            $thisComponent = $component
        }
        elseif ($PsCmdlet.ParameterSetName -eq 'ById')
        {
            $thisComponent = Get-StatuspageComponent -id $componentId
        }

        if ($thisComponent.group -eq $false)
        {
            Invoke-StatuspageRestMethod -page "components/$($thisComponent.id)" -Method Delete -retryCount $retryCount -retrySecDelay $retrySecDelay
        }
    }
}

function Get-StatuspageIncident
{
    [CmdletBinding(DefaultParameterSetName = 'NoneParameterSetName')]
    param
    (
        [parameter(ValueFromPipeline, ParameterSetName = 'ByComponent')]
        [PSTypeName('statuspageComponent')] $component,

        [parameter(ParameterSetName = 'ByQuery')]
        [string] $query,

        [parameter(ParameterSetName = 'ByComponent')]
        [parameter(ParameterSetName = 'ByQuery')]
        [string] $status,

        [parameter(ParameterSetName = 'ByComponent')]
        [switch] $queryComponentName,

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )

    process
    {
        $page = 'incidents'
        if ($queryComponentName)
        {
            $page += "?q=$($component.name)"
        }
        elseif ($query)
        {
            $page += "?q=$query"
        }

        $incidents = Invoke-StatuspageRestMethod -page $page -Method Get -retryCount $retryCount -retrySecDelay $retrySecDelay | Convert-StatuspageIncident
        
        if ($component)
        {
            $incidents = $incidents | Where-Object { $_.components.id -eq $component.id }
        }
        if ($status)
        {
            $incidents | Where-Object { $_.status -eq $status }
        }
        else
        {
            $incidents
        }
    }
}

function New-StatuspageIncident
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [parameter(Mandatory, ParameterSetName = 'ById')]
        [string] $name,

        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [parameter(Mandatory, ParameterSetName = 'ById')]
        [string] $body,

        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [parameter(Mandatory, ParameterSetName = 'ById')]
        [ValidateSet('investigating', 'identified', 'monitoring', 'resolved', 'scheduled', 'in_progress', 'verifying', 'completed', 'update')]
        [string] $status,

        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [PSTypeName('statuspageComponent')] $component,

        [parameter(Mandatory, ParameterSetName = 'ById')]
        [string[]] $componentId,

        [parameter(ParameterSetName = 'ByObject')]
        [parameter(ParameterSetName = 'ById')]
        [ValidateSet('operational', 'major_outage', 'degraded_performance', 'partial_outage', 'under_maintenance')]
        [string] $componentStatus,

        [parameter(ParameterSetName = 'ByObject')]
        [parameter(ParameterSetName = 'ById')]
        [dateTime] $scheduledFor,

        [parameter(ParameterSetName = 'ByObject')]
        [parameter(ParameterSetName = 'ById')]
        [dateTime] $scheduledUntil,

        [parameter(ParameterSetName = 'ByObject')]
        [parameter(ParameterSetName = 'ById')]
        [switch] $deliverNotifications,

        [parameter(ParameterSetName = 'ByObject')]
        [parameter(ParameterSetName = 'ById')]
        [switch] $scheduledRemindPrior,

        [parameter(ParameterSetName = 'ByObject')]
        [parameter(ParameterSetName = 'ById')]
        [switch] $scheduledAutoCompleted,

        [parameter(ParameterSetName = 'ByObject')]
        [parameter(ParameterSetName = 'ById')]
        [switch] $scheduledAutoInProgress,

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )
    
    if ($PsCmdlet.ParameterSetName -eq 'ByObject')
    {
        $thisComponentId = $component.id
    }
    elseif ($PsCmdlet.ParameterSetName -eq 'ById')
    {
        $thisComponentId = $componentId
    }

    $thisIncident = @{
        name = $name
        body = $body
        status = $status
        component_ids = $thisComponentId
    }
    if ($componentStatus)
    {
        $thisIncident.components = $thisComponentId | ForEach-Object { @{ $_ = $componentStatus } }
    }
    if ($status -in 'in_progress', 'scheduled')
    {
        if ($scheduledFor -eq $null -or $scheduledUntil -eq $null)
        {
            throw "status=$status require parameters `$scheduledFor and `$scheduledUntil"
        }
        else
        {
            $thisIncident.scheduled_for = $scheduledFor | Get-Date -Format "yyyy-MM-dd'T'HH:mm:00.000'Z'"
            $thisIncident.scheduled_until = $scheduledUntil | Get-Date -Format "yyyy-MM-dd'T'HH:mm:00.000'Z'"
        }
    }
    $thisIncident.scheduled_remind_prior = $scheduledRemindPrior.IsPresent
    $thisIncident.scheduled_auto_completed = $scheduledAutoCompleted.IsPresent
    $thisIncident.scheduled_auto_in_progress = $scheduledAutoInProgress.IsPresent
    $thisIncident.deliver_notifications = $deliverNotifications.IsPresent

    Invoke-StatuspageRestMethod -page incidents -method Post -retryCount $retryCount -retrySecDelay $retrySecDelay -body @{
        incident = $thisIncident
    } | Convert-StatuspageIncident
}

function Update-StatuspageIncident
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [PSTypeName('statuspageIncident')] $incident,

        [parameter(Mandatory, ParameterSetName = 'ById')]
        [string] $incidentId,

        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [parameter(Mandatory, ParameterSetName = 'ById')]
        [string] $body,

        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [parameter(Mandatory, ParameterSetName = 'ById')]
        [ValidateSet('investigating', 'identified', 'monitoring', 'resolved', 'scheduled', 'in_progress', 'verifying', 'completed', 'update')]
        [string] $status,

        [parameter(Mandatory, ParameterSetName = 'ByObject')]
        [PSTypeName('statuspageComponent')] $component,

        [parameter(Mandatory, ParameterSetName = 'ById')]
        [string[]] $componentId,

        [parameter(ParameterSetName = 'ByObject')]
        [parameter(ParameterSetName = 'ById')]
        [ValidateSet('operational', 'major_outage', 'degraded_performance', 'partial_outage', 'under_maintenance')]
        [string] $componentStatus,

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )

    if ($PsCmdlet.ParameterSetName -eq 'ByObject')
    {
        $thisComponentId = $component.id
        $thisIncidentId = $incident.id
    }
    elseif ($PsCmdlet.ParameterSetName -eq 'ById')
    {
        $thisComponentId = $componentId
        $thisIncidentId = $incidentId
    }

    $thisIncident = @{
        body = $body
        status = $status
        component_ids = $thisComponentId
    }
    if ($componentStatus)
    {
        $thisIncident.components = $thisComponentId | ForEach-Object { @{ $_ = $componentStatus } }
    }

    Invoke-StatuspageRestMethod -page "incidents/$thisIncidentId" -method Patch -retryCount $retryCount -retrySecDelay $retrySecDelay -body @{
        incident = $thisIncident
    } | Convert-StatuspageIncident
}

function Remove-StatuspageIncident
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByObject')]
        [PSTypeName('statuspageIncident')] $incident,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ById')]
        [string] $incidentId,

        [int] $retryCount = 15,

        [int] $retrySecDelay = 15
    )

    process
    {
        if ($PsCmdlet.ParameterSetName -eq 'ByObject')
        {
            $thisIncidentId = $incident.id
        }
        elseif ($PsCmdlet.ParameterSetName -eq 'ById')
        {
            $thisIncidentId = $incidentId
        }

        Invoke-StatuspageRestMethod -page "incidents/$thisIncidentId" -Method Delete -retryCount $retryCount -retrySecDelay $retrySecDelay | Convert-StatuspageIncident
    }
}