Get-AzureAutomationDiagnosticResults.ps1


<#PSScriptInfo
 
.VERSION 1.0.3.2
 
.GUID 5922fab0-f90c-41a8-a59b-be5409271e6e
 
.AUTHOR Jeffrey Fanjoy
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS Azure Automation AzureAutomationNotSupported
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
#>


<#
 
.DESCRIPTION
Capture diagnostic information for Azure Automation accounts.
 
NOTE: While the script is for Azure Automation, it is not supported to run in Azure Automation.
 
#>
 

<#
.SYNOPSIS
    Get information about Azure Automation accounts and the content.
 
.DESCRIPTION
    This script will enumerate Automation accounts and capture information about
    the account as well as the contents under the account.
 
    - Details about the Automation account.
    - Details about the Module assets.
    - Details about the Variable assets.
    - Details about the Connection assets.
    - Details about the Credential assets.
    - Details about the Schedule assets.
    - Details about the scheduled runbooks (schedules linked to runbooks).
    - Summary details about each runbook.
    - Export of each runbook.
    - Summary details of last N jobs (see NumberOfJobs parameter).
    - Summary details of job stream data for last N jobs.
    - Details of job stream values data (Error streams only by default, see
      IncludeAllStreamValues parameter).
 
    Results will be written to $env:TEMP\AzureAutomationDiagnostics\yyyyMMddHHmmss.
    The script will open File Explorer to that location when it has completed.
 
.PARAMETER Environment
    Optional. Identifies the Azure environment that is to be used. By default the
    environment AzureCloud is used.
 
    Available options are:
 
        - AzureCloud
        - AzureUSGovernment
 
.PARAMETER AutomationAccountNames
    Optional. An array of Automation account names to be processed. By default
    all Automation accounts in the subscription will be included.
 
.PARAMETER RunbookNames
    Optional. An array of Runbook names to be processed. By default all Runbooks
    in each Automation account are included. Since Runbooks are referenced by
    name, if the same Runbook name exists in more than one Automation account it
    will be processed in each account.
 
.PARAMETER JobIds
    Optional. An array of Job identifiers to be processed. By default the last N
    Jobs in each Automation account are included (see NumberOfJobs parameter). If
    Job identifiers are provided, then only Jobs matching those identifiers will be
    processed even across all Automation accounts. Result may be that some
    Automation accounts have no Jobs included in the results because they didn't
    have any matching Jobs.
 
.PARAMETER IncludeAllStreamValues
    Optional. By default, stream values are only included for "Error" streams. The
    summary of all job streams is captured, but capturing the full values of streams
    is a very performance intensive process as it has to make a web service call for
    every stream.
 
    THIS PARAMETER CAN CAUSE THE SCRIPT TO TAKE A VERY LONG TIME TO COMPLETE IF OTHER
    PARAMETERS ARE NOT INCLUDED TO SCOPE DOWN THE RUNBOOKS/JOBS THAT ARE PROCESSED.
 
.PARAMETER NumberOfJobs
    Optional. By default, the last 5 jobs for each runbook in each Automation
    account are processed. This parameter defines the last N jobs that will be
    processed for each runbook.
 
.PARAMETER NumberOfDays
    Optional. By default, the last 7 days of jobs are processed. This parameter
    defines the last N days of jobs that will be processed.
 
.EXAMPLE
    .\Get-AutomationDiagnosticResults.ps1
 
    The above will process all Automation accounts, Runbooks and last N Jobs in the
    chosen subscription.
 
.EXAMPLE
    .\Get-AutomationDiagnosticResults.ps1 -AutomationAccounts 'MyAutomationAccount'
 
    The above will process all Runbooks and last N Jobs in the Automation account
    named 'MyAutomationAccount' only.
 
.EXAMPLE
    .\Get-AutomationDiagnosticResults.ps1 -AutomationAccounts @('MyFirstAutomationAccount','MySecondAutomationAccount') -NumberOfJobs 20 -IncludeAllStreamValues
 
    The above will process all Runbooks and last 20 Jobs in the Automation accounts
    'MyFirstAutomationAccount' and 'MySecondAutomationAccount' and will include full
    stream values for all stream types for each of the last 20 Jobs.
 
.EXAMPLE
    .\Get-AutomationDiagnosticResults.ps1 -RunbookNames 'MyRunbook'
 
    The above will process only runbooks named 'MyRunbook' in each of the Automation
    accounts and will only include Job results for that runbook. Summary details
    about all the Automation accounts will still be included such as Module and
    Schedule assets.
 
.EXAMPLE
    .\Get-AutomationDiagnosticResults.ps1 -AutomationAccountNames 'MyAutomationAccount' -RunbookNames @('MyFirstRunbook','MySecondRunbook')
     
    The above will process only Runbooks named 'MyFirstRunbook' and 'MySecondRunbook'
    in the Automation account 'MyAutomationAccount'. Summary details about the
    Automation account 'MyAutomationAccount' will also be included.
 
.EXAMPLE
    .\Get-AutomationDiagnosticResults.ps1 -AutomationAccountNames 'MyAutomationAccount' -RunbookName 'MyRunbook' -JobIds '12345678-9012-3456-7890-123456789012'
 
    The above will process only the Job with the specified id and will only include
    information about the Runbook named 'MyRunbook' and only the Automation account
    named 'MyAutomationAccount'.
 
.NOTES
    AUTHOR : Jeffrey Fanjoy
 
    Requires: AzureRM
#>


Param (
    [Parameter(Mandatory=$false)]
    [ValidateSet('AzureCloud','AzureUSGovernment')]
    [Alias('EnvironmentName')]
    [string] $Environment = 'AzureCloud',
    [Parameter(Mandatory=$false)]
    [string[]] $AutomationAccountNames,
    [Parameter(Mandatory=$false)]
    [string[]] $RunbookNames,
    [Parameter(Mandatory=$false)]
    [string[]] $JobIds,
    [Parameter(Mandatory=$false)]
    [switch] $IncludeAllStreamValues,
    [Parameter(Mandatory=$false)]
    [int] $NumberOfJobs = 5,
    [Parameter(Mandatory=$false)]
    [int] $NumberOfDays = 7
)

    # Assume going in that requirements have been met unless otherwise determined
    $RequirementsMet = $true
    $RequiredModulesMet = $true

    # List of modules that are required
    $Modules = @(
        @{ Name = 'AzureRM.profile'; Version = [System.Version]'3.4.0' }
        @{ Name = 'AzureRM.automation'; Version = [System.Version]'3.4.0' }
        @{ Name = 'AzureRM.resources'; Version = [System.Version]'4.4.0' }
    )

    # Check to confirm that dependency modules are installed
    Function CheckDependencyModules {
        Write-Output ("Checking for presence of required modules.")
        foreach ($Module in $Modules) {
            if ([string]::IsNullOrEmpty($Module.Version)) {
                Write-Output ("Checking for module '{0}'." -f $Module.Name)
            } else {
                Write-Output ("Checking for module '{0}' of at least version '{1}'." -f $Module.Name, $Module.Version)
            }
            $LatestVersion = (Find-Module -Name $Module.Name).Version
            $CurrentModule = Get-Module -Name $Module.Name -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1
            if ($CurrentModule) {
                Write-Output ("Found version '{0}' of module '{1}' installed." -f $CurrentModule.Version, $CurrentModule.Name)
                if ($LatestVersion) {
                    if ($LatestVersion.Version -gt $CurrentModule.Version) {
                        Write-Output ("There is a newer version of module '{0}'. Version '{1}' is available." -f $LatestVersion.Name, $LatestVersion.Version)
                    }
                }
                if ($CurrentModule.Version -lt $Module.Version) {
                    Write-Error ("Installed version '{0}' of module '{1}' does not meet minimum requirements." -f $CurrentModule.Version, $CurrentModule.Name)
                    $script:RequirementsMet = $false
                    $script:RequiredModulesMet = $false
                }
            } else {
                Write-Error ("Could not find module '{0}' installed." -f $Module.Name)
                $script:RequirementsMet = $false
                $script:RequiredModulesMet = $false
            }
        }
    }

    Function CheckDependencies {
        Write-Output ("Checking for dependencies.")
        CheckDependencyModules
    }

    Function CreateFolder {
        Param (
            [Parameter(Mandatory=$true)]
            [string] $FolderName
        )

        if (!(Test-Path -Path $FolderName)) {
            Write-Output ("Creating folder '{0}'." -f $FolderName)
            $null = New-Item -ItemType Directory -Path $FolderName
        }
    }

    Function CreateResultFolder {
        Write-Output ("Creating folders for diagnostic results.")
        $script:BasePath = $env:TEMP
        Write-Output ("Setting BasePath = {0}." -f $BasePath)
        $script:AzureAutomationDiagBasePath = ("{0}\AzureAutomationDiagnostics" -f $BasePath)
        Write-Output ("Setting azure automation base diagnostics path '{0}'." -f $AzureAutomationDiagBasePath)
        CreateFolder $AzureAutomationDiagBasePath
        $script:AzureAutomationDiagResultPath = ("{0}\{1}" -f $AzureAutomationDiagBasePath, (Get-Date -Format 'yyyyMMddHHmmss'))
        Write-Output ("Setting azure automation diagnostics results folder '{0}'." -f $AzureAutomationDiagResultPath)
        CreateFolder $AzureAutomationDiagResultPath
    }

    Function WriteModuleDetails {
        Param (
            [Parameter(Mandatory=$true)]
            [object] $AutomationAccount,
            [Parameter(Mandatory=$true)]
            [string] $ResultsFolder
        )

        Write-Output ("Retrieving list of modules imported into Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        $ModuleList = Get-AzureRmAutomationModule -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName
        if (($ModuleList | Measure-Object).Count -eq 0) {
            Write-Output ("No modules found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Found '{0}' module(s) in Automation account '{1}'." -f ($ModuleList | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            $Modules = @()
            $ModuleList | ForEach-Object {
                Write-Output ("Getting details for module '{0}'." -f $_.Name)
                $Modules += Get-AzureRmAutomationModule -Name $_.Name -ResourceGroupName $_.ResourceGroupName -AutomationAccountName $_.AutomationAccountName
            }
            Write-Output ("Writing module summary to '{0}\ModulesSummary.txt'." -f $ResultsFolder)
            $Modules | Sort-Object Name | Select-Object Name, IsGlobal, ProvisioningState, Version, SizeInBytes, ActivityCount, CreationTime, LastModifiedTime | Format-Table * -AutoSize | Out-String -Width 8000 | Out-File ("{0}\ModulesSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing module summary in CSV to '{0}\ModulesSummary.csv'." -f $ResultsFolder)
            $Modules | Sort-Object Name | Select-Object Name, IsGlobal, ProvisioningState, Version, SizeInBytes, ActivityCount, CreationTime, LastModifiedTime | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\ModulesSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
        }
    }

    Function WriteRunbookDetails {
        Param (
            [Parameter(Mandatory=$true)]
            [object] $AutomationAccount,
            [Parameter(Mandatory=$true)]
            [string] $ResultsFolder
        )
        
        Write-Output ("Retrieving list of runbooks imported into Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        if ($RunbookNames) {
            $RunbookNames | ForEach-Object { Write-Output ("Scoping results to include runbook named '{0}'." -f $_) }
            $RunbooksList = Get-AzureRmAutomationRunbook -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName | Where-Object { $RunbookNames -contains $_.Name }
        } else {
            $RunbooksList = Get-AzureRmAutomationRunbook -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName
        }
        if (($RunbooksList | Measure-Object).Count -eq 0) {
            Write-Output ("No runbooks found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Found '{0}' runbook(s) in Automation account '{1}'." -f ($RunbooksList | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            $Runbooks = @()
            $RunbooksList | ForEach-Object {
                Write-Output ("Getting details for runbook '{0}'." -f $_.Name)
                $Runbooks += Get-AzureRmAutomationRunbook -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName -Name $_.Name
            }
            Write-Output ("Writing runbook summary to '{0}\RunbooksSummary.txt'." -f $ResultsFolder)
            $Runbooks | Sort-Object Name | Select-Object Name, RunbookType, State, JobCount, Location, CreationTime, LastModifiedTime, LastModifiedBy, LogVerbose, LogProgress | Format-Table * -AutoSize | Out-String -Width 8000 | Out-File ("{0}\RunbooksSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing runbook summary in CSV to '{0}\RunbooksSummary.csv'." -f $ResultsFolder)
            $Runbooks | Sort-Object Name | Select-Object Name, RunbookType, State, JobCount, Location, CreationTime, LastModifiedTime, LastModifiedBy, LogVerbose, LogProgress | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\RunbooksSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing runbook details in JSON to '{0}\RunbooksJSON.txt'." -f $ResultsFolder)
            $Runbooks | Sort-Object Name | ConvertTo-Json -Depth 10 | Out-File ("{0}\RunbooksJSON.txt" -f $ResultsFolder) -Encoding ascii -Force

            # Exporting runbooks
            $RunbookExportsResultFolder = ("{0}\RunbookExports" -f $AutomationAccountResultFolder)
            CreateFolder $RunbookExportsResultFolder
            $RunbookExportsPublishedResultFolder = ("{0}\Published" -f $RunbookExportsResultFolder)
            CreateFolder $RunbookExportsPublishedResultFolder
            $RunbookExportsDraftResultFolder = ("{0}\Draft" -f $RunbookExportsResultFolder)
            CreateFolder $RunbookExportsDraftResultFolder
            Write-Output ("Exporting published runbooks to folder '{0}'." -f $RunbookExportsPublishedResultFolder)
            $Runbooks | Where-Object { $_.State -ne 'New' } | ForEach-Object {
                Write-Output ("Exporting published version of runbook '{0}' to '{1}'." -f $_.Name, $RunbookExportsPublishedResultFolder)
                $null = Export-AzureRmAutomationRunbook -ResourceGroupName $_.ResourceGroupName -AutomationAccountName $_.AutomationAccountName -Name $_.Name -Slot Published -OutputFolder $RunbookExportsPublishedResultFolder -Force
            }
            Write-Output ("Exporting draft runbooks to folder '{0}'." -f $RunbookExportsPublishedResultFolder)
            $Runbooks | Where-Object { $_.State -ne 'Published' } | ForEach-Object {
                Write-Output ("Exporting draft version of runbook '{0}' to '{1}'." -f $_.Name, $RunbookExportsDraftResultFolder)
                $null = Export-AzureRmAutomationRunbook -ResourceGroupName $_.ResourceGroupName -AutomationAccountName $_.AutomationAccountName -Name $_.Name -Slot Draft -OutputFolder $RunbookExportsDraftResultFolder -Force
            }
        }
    }

    Function WriteScheduleDetails {
        Param (
            [Parameter(Mandatory=$true)]
            [object] $AutomationAccount,
            [Parameter(Mandatory=$true)]
            [string] $ResultsFolder
        )

        Write-Output ("Retrieving list of schedules in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        $ScheduleList = Get-AzureRmAutomationSchedule -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName
        if (($ScheduleList | Measure-Object).Count -eq 0) {
            Write-Output ("No schedules found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Found '{0}' schedule(s) in Automation account '{1}'." -f ($ScheduleList | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            $Schedules = @()
            $ScheduleList | ForEach-Object {
                Write-Output ("Getting details for schedule '{0}'." -f $_.Name)
                $Schedules += Get-AzureRmAutomationSchedule -ResourceGroupName $_.ResourceGroupName -AutomationAccountName $_.AutomationAccountName -Name $_.Name
            }
            Write-Output ("Writing schedule summary to '{0}\SchedulesSummary.txt'." -f $ResultsFolder)
            $Schedules | Sort-Object Name | Select-Object Name, IsEnabled, StartTime, ExpiryTime, NextRun, Interval, Frequency, TimeZone, CreationTime, LastModifiedTime | Format-Table * -AutoSize | Out-String -Width 8000 | Out-File ("{0}\SchedulesSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing schedule summary in CSV to '{0}\SchedulesSummary.csv'." -f $ResultsFolder)
            $Schedules | Sort-Object Name | Select-Object Name, IsEnabled, StartTime, ExpiryTime, NextRun, Interval, Frequency, TimeZone, CreationTime, LastModifiedTime | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\SchedulesSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing schedule details in JSON to '{0}\SchedulesJSON.txt'." -f $ResultsFolder)
            $Schedules | Sort-Object Name | ConvertTo-Json -Depth 10 | Out-File ("{0}\SchedulesJSON.txt" -f $ResultsFolder) -Encoding ascii -Force
        }
    }

    Function WriteScheduledRunbookDetails {
        Param (
            [Parameter(Mandatory=$true)]
            [object] $AutomationAccount,
            [Parameter(Mandatory=$true)]
            [string] $ResultsFolder
        )

        Write-Output ("Retrieving list of scheduled runbooks in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        $ScheduledRunbookList = Get-AzureRmAutomationScheduledRunbook -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName
        if (($ScheduledRunbookList | Measure-Object).Count -eq 0) {
            Write-Output ("No scheduled runbooks found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Found '{0}' scheduled runbook(s) in Automation account '{1}'." -f ($ScheduledRunbookList | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            $ScheduledRunbooks = @()
            $ScheduledRunbookList | ForEach-Object {
                Write-Output ("Getting details for scheduled runbook job schedule id '{0}' of schedule '{1}' against runbook '{2}'." -f $_.JobScheduleId, $_.ScheduleName, $_.RunbookName)
                $ScheduledRunbooks += Get-AzureRmAutomationScheduledRunbook -ResourceGroupName $_.ResourceGroupName -AutomationAccountName $_.AutomationAccountName -JobScheduleId $_.JobScheduleId
            }
            Write-Output ("Writing scheduled job summary to '{0}\ScheduledRunbooksSummary.txt'." -f $ResultsFolder)
            $ScheduledRunbooks | Sort-Object ScheduleName | Select-Object ScheduleName, RunbookName, JobScheduleId, RunOn | Format-Table * -AutoSize | Out-String -Width 8000 | Out-File ("{0}\ScheduledRunbooksSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing scheduled job summary in CSV to '{0}\ScheduledRunbooksSummary.csv'." -f $ResultsFolder)
            $ScheduledRunbooks | Sort-Object ScheduleName | Select-Object ScheduleName, RunbookName, JobScheduleId, RunOn | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\ScheduledRunbooksSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing scheduled job details in JSON to '{0}\ScheduledRunbooksJSON.txt'." -f $ResultsFolder)
            $ScheduledRunbooks | Sort-Object ScheduleName | ConvertTo-Json -Depth 10 | Out-File ("{0}\ScheduledRunbooksJSON.txt" -f $ResultsFolder) -Encoding ascii -Force
        }
    }

    Function WriteVariableDetails {
        Param (
            [Parameter(Mandatory=$true)]
            [object] $AutomationAccount,
            [Parameter(Mandatory=$true)]
            [string] $ResultsFolder
        )

        Write-Output ("Retrieving list of variables in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        $AutomationVariables = Get-AzureRmAutomationVariable -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName
        if (($AutomationVariables | Measure-Object).Count -eq 0) {
            Write-Output ("No variables found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Found '{0}' variable(s) in Automation account '{1}'." -f ($AutomationVariables | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            Write-Output ("Writing variables summary to '{0}\VariablesSummary.txt'." -f $ResultsFolder)
            $AutomationVariables | Sort-Object Name | Select-Object Name, Encrypted, Value, CreationTime, LastModifiedTime, Description | Format-Table -AutoSize | Out-String -Width 8000 | Out-File ("{0}\VariablesSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing variables summary in CSV to '{0}\VariablesSummary.csv'." -f $ResultsFolder)
            $AutomationVariables | Sort-Object Name | Select-Object Name, Encrypted, Value, CreationTime, LastModifiedTime, Description | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\VariablesSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
        }
    }

    Function WriteCredentialDetails {
        Param (
            [Parameter(Mandatory=$true)]
            [object] $AutomationAccount,
            [Parameter(Mandatory=$true)]
            [string] $ResultsFolder
        )

        Write-Output ("Retrieving list of credentials in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        $AutomationCredentials = Get-AzureRmAutomationCredential -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName
        if (($AutomationCredentials | Measure-Object).Count -eq 0) {
            Write-Output ("No credentials found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Found '{0}' credential(s) in Automation account '{1}'." -f ($AutomationCredentials | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            Write-Output ("Writing credentials summary to '{0}\CredentialsSummary.txt'." -f $ResultsFolder)
            $AutomationCredentials | Sort-Object Name | Select-Object Name, UserName, CreationTime, LastModifiedTime, Description | Format-Table -AutoSize | Out-String -Width 8000 | Out-File ("{0}\CredentialsSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing credentials summary in CSV to '{0}\CredentialsSummary.csv'." -f $ResultsFolder)
            $AutomationCredentials | Sort-Object Name | Select-Object Name, UserName, CreationTime, LastModifiedTime, Description | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\CredentialsSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
        }
    }

    Function WriteCertificateDetails {
        Param (
            [Parameter(Mandatory=$true)]
            [object] $AutomationAccount,
            [Parameter(Mandatory=$true)]
            [string] $ResultsFolder
        )

        Write-Output ("Retrieving list of certificates in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        $AutomationCertificates = Get-AzureRmAutomationCertificate -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName
        if (($AutomationCertificates | Measure-Object).Count -eq 0) {
            Write-Output ("No certificates found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Found '{0}' certificate(s) in Automation account '{1}'." -f ($AutomationCertificates | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            Write-Output ("Writing certificates summary to '{0}\CertificatesSummary.txt'." -f $ResultsFolder)
            $AutomationCertificates | Sort-Object Name | Select-Object Name, Exportable, ExpiryTime, Thumbprint, CreationTime, LastModifiedTime, Description | Format-Table -AutoSize | Out-String -Width 8000 | Out-File ("{0}\CertificatesSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing certificates summary in CSV to '{0}\CertificatesSummary.csv'." -f $ResultsFolder)
            $AutomationCertificates | Sort-Object Name | Select-Object Name, Exportable, ExpiryTime, Thumbprint, CreationTime, LastModifiedTime, Description | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\CertificatesSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
        }
    }

    Function WriteConnectionDetails {
        Param (
            [Parameter(Mandatory=$true)]
            [object] $AutomationAccount,
            [Parameter(Mandatory=$true)]
            [string] $ResultsFolder
        )

        Write-Output ("Retrieving list of connections in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        $ConnectionList = Get-AzureRmAutomationConnection -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName
        if (($ConnectionList | Measure-Object).Count -eq 0) {
            Write-Output ("No connections found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Found '{0}' connection(s) in Automation account '{1}'." -f ($ConnectionList | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            $Connections = @()
            $ConnectionList | ForEach-Object {
                Write-Output ("Getting details for connection '{0}'." -f $_.Name)
                $Connections += Get-AzureRmAutomationConnection -ResourceGroupName $_.ResourceGroupName -AutomationAccountName $_.AutomationAccountName -Name $_.Name
            }
            Write-Output ("Writing connection summary to '{0}\ConnectionsSummary.txt'." -f $ResultsFolder)
            $Connections | Sort-Object Name | Select-Object Name, ConnectionTypeName, CreationTime, LastModifiedTime | Format-Table * -AutoSize | Out-String -Width 8000 | Out-File ("{0}\ConnectionsSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing connection summary in CSV to '{0}\ConnectionsSummary.csv'." -f $ResultsFolder)
            $Connections | Sort-Object Name | Select-Object Name, ConnectionTypeName, CreationTime, LastModifiedTime | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\ConnectionsSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing connection details in JSON to '{0}\ConnectionsJSON.txt'." -f $ResultsFolder)
            $Connections | Sort-Object Name | ConvertTo-Json -Depth 10 | Out-File ("{0}\ConnectionsJSON.txt" -f $ResultsFolder) -Encoding ascii -Force
        }
    }

    Function WriteJobDetails {
        Param (
            [Parameter(Mandatory=$true)]
            [object] $AutomationAccount,
            [Parameter(Mandatory=$true)]
            [string] $ResultsFolder
        )

        Write-Output ("Retrieving list of runbooks imported into Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        if ($RunbookNames) {
            $RunbookNames | ForEach-Object { Write-Output ("Scoping results to include runbook named '{0}'." -f $_) }
            $RunbooksList = Get-AzureRmAutomationRunbook -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName | Where-Object { $RunbookNames -contains $_.Name }
        } else {
            $RunbooksList = Get-AzureRmAutomationRunbook -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName 
        }
        if (($RunbooksList | Measure-Object).Count -eq 0) {
            Write-Output ("No runbooks found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Found '{0}' runbook(s) in Automation account '{1}'." -f ($RunbooksList | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            Write-Output ("Retrieving jobs that have executed in the last '{0}' day(s) from Automation account '{1}'." -f $NumberOfDays, $AutomationAccount.AutomationAccountName)
            $JobsLastNDays = Get-AzureRmAutomationJob -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName -StartTime ((Get-Date).AddDays(-$NumberOfDays)) 
            Write-Output ("Retrieved a total of '{0}' job(s) from Automation account '{1}'." -f ($JobsLastNDays | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            $JobsList = @()
            foreach ($Runbook in $RunbooksList) {
                Write-Output ("Filtering last '{0}' job(s) for runbook '{1}' in Automation account '{2}' having executed in the last '{3}' day(s)." -f $NumberOfJobs, $Runbook.Name, $AutomationAccount.AUtomationAccountName, $NumberOfDays)
                $RunbookJobsList = $JobsLastNDays | Where-Object { $_.RunbookName -eq $Runbook.Name } | Sort-Object CreationTime | Select-Object -Last $NumberOfJobs | Sort-Object CreationTime -Descending
                Write-Output ("Filtered '{0}' job(s) for runbook '{1}'." -f ($RunbookJobsList | Measure-Object).Count, $Runbook.Name)
                $JobsList += $RunbookJobsList
            }
        }

        if (($JobsList | Measure-Object).Count -eq 0) {
            Write-Output ("No jobs found in Automation account '{0}'." -f $AutomationAccount.AutomationAccountName)
        } else {
            Write-Output ("Retrieved '{0}' job(s) in Automation account '{1}'." -f ($JobsList | Measure-Object).Count, $AutomationAccount.AutomationAccountName)
            $Jobs = @()
            $JobsList | Sort-Object CreationTime -Descending | ForEach-Object {
                Write-Output ("Getting details for job id '{0}' of runbook '{1}'." -f $_.JobId, $_.RunbookName)
                $Jobs += Get-AzureRmAutomationJob -ResourceGroupName $_.ResourceGroupname -AutomationAccountName $_.AutomationAccountName -Id $_.JobId
            }
            Write-Output ("Writing job summary to '{0}\JobsSummary.txt'." -f $ResultsFolder)
            $Jobs | Select-Object JobId, RunbookName, Status, StatusDetails, HybridWorker, StartedBy, CreationTime, StartTime, EndTime, @{Name="Duration";Expression={$_.EndTime - $_.StartTime}}, Exception | Format-Table * -AutoSize | Out-String -Width 8000 | Out-File ("{0}\JobsSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing job summary in CSV to '{0}\JobsSummary.csv'." -f $ResultsFolder)
            $Jobs | Select-Object JobId, RunbookName, Status, StatusDetails, HybridWorker, StartedBy, CreationTime, StartTime, EndTime, @{Name="Duration";Expression={$_.EndTime - $_.StartTime}}, Exception | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\JobsSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing job details in JSON to '{0}\JobsJSON.txt'." -f $ResultsFolder)
            $Jobs | Select-Object *, @{Name="Duration";Expression={$_.EndTime - $_.StartTime}} | ConvertTo-Json -Depth 10 | Out-File ("{0}\JobsJSON.txt" -f $ResultsFolder) -Encoding ascii -Force

            # Process each job to capture job stream data
            $JobStreamRecords = @()
            $Jobs | ForEach-Object {
                Write-Output ("Retrieving job streams for job id '{0}' of runbook '{1}'." -f $_.JobId, $_.RunbookName)
                $JobStreams = $_ | Get-AzureRmAutomationJobOutput -Stream Any | Sort-Object StreamRecordId
                if (($JobStreams | Measure-Object).Count -eq 0) {
                    Write-Output ("No job streams found for job id '{0}'." -f $_.JobId)
                } else {
                    Write-Output ("Found '{0}' job streams for job id '{1}'." -f ($JobStreams | Measure-Object).Count, $_.JobId)
                    foreach ($JobStream in $JobStreams) {
                        $JobStreamRecord = $_
                        Add-Member -InputObject $JobStreamRecord -MemberType NoteProperty -Name StreamRecordId -Value $JobStream.StreamRecordId -Force
                        Add-Member -InputObject $JobStreamRecord -MemberType NoteProperty -Name StreamTime -Value $JobStream.Time -Force
                        Add-Member -InputObject $JobStreamRecord -MemberType NoteProperty -Name StreamType -Value $JobStream.Type -Force
                        Add-Member -InputObject $JobStreamRecord -MemberType NoteProperty -Name StreamSummary -Value $JobStream.Summary -Force
                        if ($IncludeAllStreamValues) {
                            Write-Output ("Getting job stream record for job stream id '{0}'." -f $JobStream.StreamRecordId)
                            $OutputRecord = $JobStream | Get-AzureRmAutomationJobOutputRecord
                            Add-Member -InputObject $JobStreamRecord -MemberType NoteProperty -Name StreamValue -Value $OutputRecord.Value -Force
                        } else {
                            if ($JobStream.Type -eq 'Error') {
                                Write-Output ("Getting job stream record for job stream id '{0}'." -f $JobStream.StreamRecordId)
                                $OutputRecord = $JobStream | Get-AzureRmAutomationJobOutputRecord
                                Add-Member -InputObject $JobStreamRecord -MemberType NoteProperty -Name StreamValue -Value $OutputRecord.Value -Force
                            }
                        }
                        $JobStreamRecords += $JobStreamRecord | Select-Object RunbookName, JobId, Status, StreamRecordId, StreamTime, StreamType, StreamSummary, StreamValue
                    }
                }
            }
            Write-Output ("Writing job stream summary to '{0}\JobStreamsSummary.txt'." -f $ResultsFolder)
            $JobStreamRecords | Select-Object RunbookName, JobId, Status, StreamRecordId, StreamTime, StreamType, StreamSummary | Format-Table * -AutoSize | Out-String -Width 8000 | Out-File ("{0}\JobStreamsSummary.txt" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing job stream summary in CSV to '{0}\JobStreamsSummary.csv'." -f $ResultsFolder)
            $JobStreamRecords | Select-Object RunbookName, JobId, Status, StreamRecordId, StreamTime, StreamType, StreamSummary | ConvertTo-Csv -NoTypeInformation | Out-File ("{0}\JobStreamsSummary.csv" -f $ResultsFolder) -Encoding ascii -Force
            Write-Output ("Writing job stream values in JSON to '{0}\JobStreamsValues.txt'." -f $ResultsFolder)
            if ($IncludeAllStreamValues) {
                $JobStreamRecords | Select-Object StreamRecordId, StreamValue | ConvertTo-Json -Depth 10 | Out-File ("{0}\JobStreamsValues.txt" -f $ResultsFolder) -Encoding ascii -Force
            } else {
                $JobStreamRecords | Where-Object { $_.StreamType -eq 'Error' } | Select-Object StreamRecordId, StreamValue | ConvertTo-Json -Depth 10 | Out-File ("{0}\JobStreamsValues.txt" -f $ResultsFolder) -Encoding ascii -Force
            }
        }
    }


# Create folder structure needed for results
CreateResultFolder
Start-Transcript -Path ("{0}\Transcript.txt" -f $AzureAutomationDiagResultPath)

# Confirm that all dependencies are available and if not, install where
# possible
CheckDependencies
if (!$RequirementsMet) { 
    Write-Error ("Script execution aborted as minimum requirements have not been met. See tracing above for details.") 
    if (!$RequiredModulesMet) {
        Write-Warning ("To install latest version of AzureRM cmdlets, see: https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps")
    }
    Stop-Transcript
    Break
}

# Login to Azure.
Write-Output ("Prompting user to login to Azure environment '{0}'." -f $Environment)
$account = Add-AzureRmAccount -Environment $Environment
if (!($account)) {
    throw ("Unable to successfully authenticate to Azure for environment '{0}'." -f $Environment)
}

# Select subscription if more than one is available
Write-Output ("Selecting desired Azure Subscription.")
$Subscriptions = Get-AzureRmSubscription
Write-Output ("Found {0} subscription(s)." -f ($Subscriptions | Measure-Object).Count)
$Subscriptions | Format-Table -AutoSize | Out-String -Width 8000
switch (($Subscriptions | Measure-Object).Count) {
    0 { throw "No subscriptions found." }
    1 { 
        if ($Subscriptions[0].Id) {
            $AzureContext = Set-AzureRmContext -SubscriptionId $Subscriptions[0].Id
        } else {
            $AzureContext = Set-AzureRmContext -SubscriptionId $Subscriptions[0].SubscriptionId
        }
    }
    default { 
        Write-Output ("Multiple Subscriptions found, prompting user to select desired Azure Subscription.")
        $Subscription = ($Subscriptions | Out-GridView -Title 'Select Azure Subscription' -PassThru)
        if ($Subscription.Id) {
            $AzureContext = Set-AzureRmContext -SubscriptionId $Subscription.Id
        } else {
            $AzureContext = Set-AzureRmContext -SubscriptionId $Subscription.SubscriptionId
        }
    }
}
Write-Output ("Subscription successfully selected.")
$AzureContext | Format-List

# Get list of Automation accounts to be processed
if ($AutomationAccountNames) {
    $AutomationAccountNames | ForEach-Object { Write-Output("Scoping results to include Automation account '{0}'." -f $_) }
    $AutomationAccountsResults = Get-AzureRmAutomationAccount | Where-Object { $AutomationAccountNames -contains $_.AutomationAccountName } | Sort-Object AutomationAccountName
} else {
    Write-Output ("Retrieving list of Automation accounts.")
    $AutomationAccountsResults = Get-AzureRmAutomationAccount | Sort-Object AutomationAccountName
}

# Retrieve all details for each automation account
$AutomationAccounts = @()
$AutomationAccountsResults | ForEach-Object {
    Write-Output ("Getting details for Automation account '{0}'." -f $_.AutomationAccountName)
    $AutomationAccounts += Get-AzureRmAutomationAccount -ResourceGroupName $_.ResourceGroupName -Name $_.AutomationAccountName
}

# Write Automation accounts out to results folder
$AutomationAccountsResultsFile = ("{0}\AutomationAccounts.txt" -f $AzureAutomationDiagResultPath)
Write-Output ("Writing Azure automation account details to '{0}'." -f $AutomationAccountsResultsFile)
$AutomationAccounts | Format-Table * -AutoSize | Out-String -Width 8000 | Out-File $AutomationAccountsResultsFile -Encoding ascii -Force

# Enumerate through the Automation accounts
$AutomationAccounts | ForEach-Object {
    $AutomationAccountResultFolder = ("{0}\{1}" -f $AzureAutomationDiagResultPath, $_.AutomationAccountName)
    CreateFolder $AutomationAccountResultFolder
    WriteModuleDetails -AutomationAccount $_ -ResultsFolder $AutomationAccountResultFolder
    WriteRunbookDetails -AutomationAccount $_ -ResultsFolder $AutomationAccountResultFolder
    WriteScheduleDetails -AutomationAccount $_ -ResultsFolder $AutomationAccountResultFolder
    WriteScheduledRunbookDetails -AutomationAccount $_ -ResultsFolder $AutomationAccountResultFolder
    WriteVariableDetails -AutomationAccount $_ -ResultsFolder $AutomationAccountResultFolder
    WriteConnectionDetails -AutomationAccount $_ -ResultsFolder $AutomationAccountResultFolder
    WriteCertificateDetails -AutomationAccount $_ -ResultsFolder $AutomationAccountResultFolder
    WriteCredentialDetails -AutomationAccount $_ -ResultsFolder $AutomationAccountResultFolder
    WriteJobDetails -AutomationAccount $_ -ResultsFolder $AutomationAccountResultFolder
}

Write-Output ("Execution completed.")
Stop-Transcript

# Open the diagnostics result path in Explorer
start $AzureAutomationDiagResultPath